Zadania

  1. parallel.c - uproszczona implementacja GNU parallel. Dla każdej linii standardowego wejścia program uruchamia osobny podproces, który wykonuje dane polecenie. Po uruchomieniu wszystkich podprocesów czeka na ich zakończenie. ./parallel wywołuje polecenie przekazane przez argumenty, zastępując:

    1. ciąg {} linią przekazaną na standardowe wejście,

    2. ciąg {.} linią przekazaną na standardowe wejście bez rozszerzenia (jeśli takie występowało)

    3. ciąg {/} nazwą pliku (basename) przekazaną na standardowe wejście

    4. ciąg {//} ścieżką bez nazwy pliku (dirname) przekazaną na standardowe wejście

Testowanie parallel.c:

$ # Stworzenie pliku o rozmiarze 1GiB:
$ dd if=/dev/urandom of=1g.dat bs=1G count=1
$ time wc -w 1g.dat 1g.dat
  24590384 1g.dat
  24590384 1g.dat
  49180768 total

real    1m27,955s
user    1m27,192s
sys     0m0,359s
$ time printf "1g.dat\n1g.dat\n" | parallel wc -w "{}"
24590384 1g.dat
24590384 1g.dat

real    0m51,513s
user    1m38,512s
sys     0m0,432s
$ # Należy wcześniej przygotować pliki c, które chcemy skompilować. Najpierw możemy wypisać komendę:
$ ls *.c | parallel echo cc -c -o "{.}.o" "{}"
cc -c -o lapi.o lapi.c
cc -c -o lauxlib.o lauxlib.c
cc -c -o lbaselib.o lbaselib.c
cc -c -o lcode.o lcode.c
cc -c -o lcorolib.o lcorolib.c
cc -c -o lctype.o lctype.c
cc -c -o ldblib.o ldblib.c
cc -c -o ldebug.o ldebug.c
cc -c -o ldo.o ldo.c
cc -c -o ldump.o ldump.c
cc -c -o lfunc.o lfunc.c
cc -c -o lgc.o lgc.c
cc -c -o linit.o linit.c
cc -c -o liolib.o liolib.c
cc -c -o llex.o llex.c
cc -c -o lmathlib.o lmathlib.c
cc -c -o lmem.o lmem.c
cc -c -o loadlib.o loadlib.c
cc -c -o lobject.o lobject.c
cc -c -o lopcodes.o lopcodes.c
cc -c -o loslib.o loslib.c
cc -c -o lparser.o lparser.c
cc -c -o lstate.o lstate.c
cc -c -o lstring.o lstring.c
cc -c -o lstrlib.o lstrlib.c
cc -c -o ltable.o ltable.c
cc -c -o ltablib.o ltablib.c
cc -c -o ltm.o ltm.c
cc -c -o lua.o lua.c
cc -c -o luac.o luac.c
cc -c -o lundump.o lundump.c
cc -c -o lutf8lib.o lutf8lib.c
cc -c -o lvm.o lvm.c
cc -c -o lzio.o lzio.c
$ # a potem wykonać
$ ls *.c | parallel cc -c -o "{.}.o" "{}"
$ cat paths.txt
/some/path
/some/other/path.txt
foo
bar.c
bar.tar.gz
$ parallel echo "line={} without-ext={.} basename={/} dirname={//}"  <paths.txt
line=/some/path without-ext=/some/path basename=path dirname=/some
line=/some/other/path.txt without-ext=/some/other/path basename=path.txt dirname=/some/other
line=foo without-ext=foo basename=foo dirname=.
line=bar.c without-ext=bar basename=bar.c dirname=.
line=bar.tar.gz without-ext=bar.tar basename=bar.tar.gz dirname=.

Procesy (Linux)

Podprocesy

#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int fib(int n)
{
	return n < 2 ? n : fib(n-1) + fib(n-2);
}

int main()
{
	for (int i = 30; i < 42; ++i) {
		if (fork() == 0) {
			printf("fib(%d) = %d\n", i, fib(i));
			return 0;
		}
	}

	while (wait(NULL) > 0 || errno != ECHILD) {}
	return 0;
}

Wykonywanie programu

  • Rodzina funkcji exec

#include <unistd.h>

int main()
{
	execl("/bin/echo", "echo", "hello, world", NULL);
}
#include <unistd.h>

int main()
{
	execlp("echo", "echo", "hello, world", NULL);
}
#include <unistd.h>
#include <string.h>

int main()
{
	char* argv[] = {
		strdup("echo"),
		strdup("hello, world"),
		NULL
	};

	execv("/bin/echo", argv);
}
#include <unistd.h>
#include <string.h>

int main()
{
	char* argv[] = {
		strdup("echo"),
		strdup("hello, world"),
		NULL
	};

	execvp("echo", argv);
}

Ścieżki / nazwy plików

Rozwiązywanie ścieżek

Są 3 formy ścieżek:

  • relatywna - ścieżka opisująca położenie pliku w stosunku do aktualnego katalogu: foo.c, ../foo.c, ./foo.c

  • bezwględna - ścieżka opisująca położenie pliku rozpoczynając od korzenia systmeu plików /foo.c, /foo/../foo.c

  • kanoniczna bezwględna - ścieżka nie wykorzystująca dowiązań symboliccznych, nadmiarowych znaków / oraz relatywnych nazw .., . (zwracana przez realpath)

Komponenty ścieżki

path       dirname   basename
/usr/lib   /usr      lib
/usr/      /         usr
usr        .         usr
/          /         /
.          .         .
..         .         ..

Funkcje dirname i basename mogą modyfikować przekazane do nich ścieżki lub wykorzystywać wewnętrzne bufory - przez co zwracane przez nie ścieżki nie powinny być przez nas przechowywane.

Kolejne wywołanie funkcji dirname/basename może nadpisać wewnętrzny bufor.