int fork(void), czyli jest mnie dwóch

W systemach uniksowych bardzo ważną funkcją jest fork(). W zasadzie ta funkcja zwraca nie tyle int, ile pid_t, ale w definicji typu pid_t jest typedef int pid_t, więc spokojnie możesz deklarować zmienne na pidy jako inty. Chociaż z drugiej strony, typ pid_t w jakimś celu został zdefiniowany, mianowicie aby kod był przenośny. W końcu nie w każdym systemie operacyjnym PIDy muszą być liczbami całkowitymi. Jeśli piszesz coś większego, to rozsądnie będzie używać typu pid_t.
Funkcja powoduje rozdzielenie procesu na dwie części: proces macierzysty (czyli ten, który wywołał forka) i proces potomny (tzw. potworek :]) Funkcja teoretycznie kopiuje przestrzeń adresową rodzica (w Linuksie kopiowane są tylko te fragmenty, w których dokonał zapisu albo rodzic, albo potworek, czyli tylko te, które się różnią, co pozwala oszczędzić i pamięć, i czas potrzebny na kopiowanie pamięci). Potworek ma wszystko to samo, co rodzic, w tym otwarte deskryptory plików i standardowe wejście/wyjście. Dlatego też przy używaniu fork() należy albo szybko wyjść z rodzica, albo zamknąć deskryptor stdin w potworku. Jednoczesne pobieranie danych scanfem w potworku i rodzicu może prowadzić do tego, że nie wiadomo, co poszło do którego procesu. O ile można to sprawdzić doświadczalnie na Linuksie, to nie będzie to kod przenośny między un*xami. Pod Linuksem proces macierzysty dostaje czas procesora przed procesami potomnymi, a na takim Solarisie odwrotnie.
Funkcja fork zwraca jako wartość pid procesu potomnego. Cały trick polega na tym, żeby rozróżnić w programie, czy jesteśmy w rodzicu, czy w potworku (proces potomny jest niemal idealną kopią [-> man fork] procesu macierzystego). Otóż fork() zwraca pid potworka tylko w rodzicu, a w potworku zwraca 0. Można to ładnie wykorzystać:

int main(void)
{
if ( fork() ) /* rodzic */
  puts("Jestem w rodzicu");
else /* potworek */
  puts("Jestem w potworku");

return 0;
}

O forku nie ma co dużo mówić, funkcja jaka jest - każdy widzi. Mogę natomiast powiedzieć o kilku innych fajnych funkcjach, które są w tej okolicy. Najpierw wait(). Deklaracja nie zachwyca, pid_t wait(int*). Funkcja czeka, aż jakiś proces potomny się zakończy, po czym zwraca jego pid, a w argumencie zapisuje kod zwrócony przez proces potomny (z paroma dodatkowymi informacjami, poczytaj man wait). Jeśli argument był NULL, to wait() nic nigdzie nie zapisuje z oczywistych powodów. Wykorzystanie?

int main(void)
{
pid_t pid, pid_z_wait;
if (( pid = fork() )) /* rodzic */
  {
  pid_z_wait = wait(NULL);
  printf("Potworek %d sie skonczyl\n", pid_z_wait);
  }
else /* potworek */
  puts("Jestem w potworku");

return 0;
}

Fakt, który potworek się skończył, robi znaczenie przy większej ilości forków.
Notka: w linijce z ifem zastosowałem podwójne nawiasy okrągłe. Kompilator może wyświetlić ostrzerzenie, jeśli w ifie dokonuje się przypisania (początkujący w C/C++ często ładują przypisanie = zamiast porównania ==). Dołożenie nawiasu wokół przypisania likwiduje to ostrzerzenie.
Co jednak, jeśli proces potomny skończy się wcześniej, niż wywoła się funkcja wait()? Taki proces funkcjonuje jako zombie, czyli już umarły, ale jeszcze chodzi ;) Taki zombie czeka tylko na wait(), żeby móc powiedzieć rodzicowi "Skończyłem się z takim a takim kodem błędu" i zdechnąć. Po zakończeniu rodzica procesem rodzicielskim niezakończonych potworków staje się init, który co jakiś czas wywołuje funkcję wait(). W przypadku procesów zombie wait() kończy się natychmiast, bez zwykłego opóźnienia wynikającego z czekania na zakończenie procesu potomnego, ale to chyba jest oczywiste.

Zwrócę jeszcze twoją uwagę na dwie podobne funkcje: pid_t waitpid(pid_t pid, int *status, int opcje) i pid_t wait4(pid_t pid, int *status, int opcje, struct rusage *zasoby). Pierwsza czeka na określonego potworka, druga robi to samo, ale zapisuje jeszcze strukturę opisującą zasoby. Wstawiając za ostatni argument NULL efekt jest identyczny, jak przy waitpid(). Po dokładniejsze informacje odsyłam do manuala.

To już wprawdzie nie są funkcje związane bezpośrednio z forkiem, ale funkcjonują :) w tych okolicach. pid_t getpid(void) i pid_t getppid(void), pierwsza zwraca pid procesu, druga zwraca pid procesu rodzicielskiego (chciałoby się napisać macierzystego, ale pisałem w rodzaju męskim, więc niech już zostanie).
To by było na tyle o forkowaniu.