Plik kernel/fork.c

int do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size)

Wewnętrzna funkcja jądra tworząca nowy proces.


Parametry

clone_flags
flagi klonowania. Najniższy bajt określa sygnał wysyłany do ojca po zakończeniu procesu potomnego (zwykle SIGCHLD). Pozostałe trzy bajty określają jakie struktury / własności ojca mają być współdzielone z procesem potomnym.
stack_start
adres stosu potomka
regs
struktura zawierająca wartości rejestrów ojca z momentu wywołania funkcji systemowej.
stack_size
rozmiar stosu. Nieużywane na większości architektur.


Algorytm do_fork

  1. Jeśli ustawiono CLONE_PID, a pid procesu jest różny od zera, zwróć błąd (-EPERM)
  2. Zaalokuj pamięć na unię task_union dla nowego procesu - funkcja alloc_task_struct(). Unia ta zawiera stos trybu jądra procesu i jego deskryptor. Odtąd na deskryptor tworzonego procesu wskazuje lokalna zmienna p.
  3. Skopiuj na p wartość deskryptora bieżącego procesu (wskazywanego przez makro current)
  4. Sprawdź czy bieżący proces nie próbuje przekroczyć przypisanego mu limitu ilości procesów (p->rlim[RLIMIT_NRPROC] <= p->user->processes). Jeśli tak, zwróć błąd -EAGAIN.
  5. Zwiększ licznik procesów użytkownika i referencji do struktury użytkownika (pola p->user->processes i p->user->__count).
  6. Jeśli przekroczono dopuszczlną liczbę procesów w systemie (nr_threads >= max_threads) zwróć błąd (-EAGAIN).
  7. Jeśli proces należy do modułowej domeny wykonania, zwiększ licznik użycia tej domeny (get_exec_domain()).
  8. Jeśli proces należy do zmodularyzowanego formatu plików wykonywalnych, zwiększ licznik odniesień do tego modułu.
  9. Wyczyść pola did_exec, swappable oraz ustal stan nowego procesu na TASK_UNINTERRUPTIBLE.
  10. Ustaw flagi nowego procesu w zależności od clone_flags - funkcja copy_flags(). Wyczyść flagi korzystania z przywilejów superużytkownika i obliczeń zmiennopozycyjnych (PF_SUPERPRIV i PF_USEDFPU). Ustaw flagę PF_FORKNOEXEC. Jeśli proces nie ma być śledzony wyczyść znacznik p->ptrace.
  11. Ustaw pola, które nie mogą być odziedziczone po ojcu
    1. Ustaw pid (get_pid).
    2. Wyzeruj wskaźniki listy procesów gotowych i najmłodszego dziecka.
    3. Zainicjuj kolejkę wait_chldexit do czekania na dzieci.
    4. Jeśli przekazano flagę CLONE_VFORK, utwórz i zainicjuj wskaźnik do struktury completion, służącej do czekania ojca na zwolnienie pamięci przez syna. W przeciwnym przypadku wyczyść to pole.
    5. Wyczyść informacje o sygnałach, które przyszły do procesu.
    6. Wyzeruj czasy spędzone przez proces w trybie jądra i użytkownika.
    7. Wyczyść flagę przywództwa sesji.
    8. Ustaw czas rozpoczęcia procesu (p->start_time=jiffies).
  12. Ustaw pola odpowiedzialne za zarządzanie otwartymi plikami, związkiem z systemem plików, pamięcią i obsługą sygnałów. Odpowiednie struktury są kopiowane z deskryptora ojca, lub, jeśli określono to w clone_flags, nowy proces bezpośrednio się do nich odwołuje zwiększając licznik referencji. W wypadku niepowodzenia zwróć wartość (-ENOMEM). Wszystkie poniższe funkcje biorą jako parametry flagi klonowania i strukturę tworzonego procesu.
    1. p->files : copy_files(). Ustawianie struktury files_struct opisującej deskryptory otwartych plików nowego procesu.
    2. p->fs : copy_fs(). Ustawianie struktury fs_struct określające miejsce wykonania w systemie plików nowego procesu.
    3. p->sig : copy_sighand(). Ustawianie struktury signal_struct odpowiedzialnej za przechowywanie funkcji obsługujących sygnały. Informacja o otrzymanych przez proces sygnałach i tych blokowanych jest trzymana bezpośrednio w task_struct (pola sigpending i blocked), więc nie podlega współdzieleniu.
    4. p->mm : copy_mm(). Funkcja kopiuje strukturę mm i ustawia wszystkim stronom znacznik tylko-do-odczytu ( o ile nie ustawiono flagi CLONE_VM). Strony pamięci nie są bezpośrednio kopiowane - stosowana jest strategia kopiowania przy zapisie.
  13. Ustal kontekst nowego procesu. Funkcja copy_thread() ustawia adres bazowy stosu jądra dziecka i kopiuje na niego wartości rejestrów ojca. Ustawia położenie stosu użytkownika na przekazany parametr. Wskaźnik instrukcji ustawia na funkję ret_from_fork i wpisuje do rejestru eax (wynik wywołania systemowego) 0. Dzięki temu pierwszą akcją procesu potomnego będzie wykonanie powrotu z funkcji systemowej i otrzyma jako wynik funkcji 0.
  14. Wyzeruj pole semundo odpowiadające ze historię operacji semaforów IPC.
  15. Ustaw p->exit_signal na podany (clone_flags & CSIGNAL oraz p->pdeath_signal na 0. Ustaw znacznik p->swappable.
  16. Rozdziel po równo dynamiczny priorytet (counter) ojca między ojca i syna, aby zachować równowagę w systemie.
  17. Ustaw pola relacji rodzicielskich (jeśli przekazano flagę CLONE_PARENT lub CLONE_THREAD to ojcem procesu potomnego zostaje ojciec procesu wywołującego funkcję do_fork()). Jeśli ustawiono CLONE_THREAD, to dodaj proces do grupy wątków ojca.
  18. Wstaw deskryptor utworzonego procesu do listy procesów (makro SET_LINKS).
  19. Wstaw deskryptor nowego procesu do tablicy haszującej pidhash- funkcja hash_pid()
  20. Zwiększ licznik procesów w systemie.
  21. Jeśli proces potomny jest śledzony, wyślij do niego sygnał SIGSTOP.
  22. Wstaw proces do listy procesów wykonywanych (runqueue) - funkcja wake_up_process().
  23. Zwiększ licznik dokonanych forków (rozwidleń?) w systemie.
  24. Jeśli ustawiono flagę CLONE_VFORK to zawieś ojca na zainicjowanej wcześniej strukturze completion dziecka - wait_for_completion(p->vfork_done). Zostanie obudzony, gdy proces potomny wywoła funkcję mm_release().
  25. Zwróć PID utworzonego procesu.


Obsługa błędów

Funkcja do_fork() ma zaimplementowaną bardzo dokładną obsługę błędów. W wypadku rozpozniania błędu nie następuje bezpośredni powrót z funkcji, lecz skok do jednej z wielu etykiet i zwolnienie posiadanych w danej chwili zasobów. Zatem nawet zakończone błędem wywołanie systemowe nie pozostawia systemu w stanie niespójnym.