fork - tworzy proces potomny
Standardowa biblioteka C (libc, -lc)
#include <unistd.h>
pid_t fork(void);
fork() tworzy nowy proces, duplikując proces
wywołujący. Nowy proces jest nazywany procesem potomnym
(lub dzieckiem). Proces wywołujący jest nazywany procesem
macierzystym (lub rodzicem).
Proces potomny i proces macierzysty działają w
oddzielnych rejonach pamięci. W momencie wykonania fork() oba
rejony pamięci mają taką samą
zawartość. Zapisy do pamięci, przypisywania
(mmap(2)) i odmapowywania (munmap(2)) pliku wykonane wobec
jednego z procesów nie mają wpływu na drugi.
Proces potomny jest dokładną kopią procesu
macierzystego z wyłączeniem następujących
punktów:
- •
- Proces potomny ma swój unikatowy identyfikator procesu i nie pasuje
ono do identyfikatora żadnej istniejącej sesji lub grupy
(setpgid(2)) procesu.
- •
- Identyfikator procesu macierzystego dla procesu potomnego jest taki sam
jak identyfikator procesu macierzystego.
- •
- Proces potomny nie dziedziczy blokad pamięci swojego procesu
macierzystego (mlock(2), mlockall(2)).
- •
- Użycie zasobów procesu (getrusage(2)) i liczniki
czasu procesora (times(2)) są resetowane do zera dla procesu
potomnego.
- •
- Zestaw oczekujących sygnałów dla procesu potomnego
jest początkowo pusty (sigpending(2)).
- •
- Proces potomny nie dziedziczy dostosowań semaforów od
procesu macierzystego (semop(2)).
- •
- Proces potomny nie dziedziczy blokad rekordów związanych z
procesem od swojego procesu macierzystego (fcntl(2)) (z drugiej
strony dziedziczy blokady opisu otwartego pliku (OFD) fcntl(2) oraz
blokady flock(2) od swojego procesu macierzystego).
- •
- Proces potomny nie dziedziczy czasomierzy od swojego procesu macierzystego
(setitimer(2), alarm(2), timer_create(2)).
- •
- Proces potomny nie dziedziczy zaległych, asynchronicznych operacji
wejścia/wyjścia od procesu macierzystego
(aio_read(3), aio_write(3)), ani nie dziedziczy
żadnego kontekstu asynchronicznego wejścia/wyjścia od
procesu macierzystego (zob. io_setup(2)).
Atrybuty procesu w powyższej liście są
określone w normie POSIX.1. Proces macierzysty i potomny
będą się różnić
również w odniesieniu do następujących, typowo
linuksowych atrybutów procesu:
- •
- Proces potomny nie odziedziczy notyfikacji o zmianie katalogu (dnotify) od
swojego rodzica (więcej informacji w opisie F_NOTIFY w
podręczniku fcntl(2)).
- •
- Ustawienie PR_SET_PDEATHSIG prctl(2) jest resetowane,
dzięki czemu proces potomny nie otrzyma sygnału, gdy jego
proces macierzysty ulegnie zakończeniu.
- •
- Domyślna wartość luzu czasomierza jest ustawiana na
aktualną wartość luzu czasomierza procesu
macierzystego. Więcej informacji w opisie PR_SET_TIMERSLACK
w podręczniku prctl(2).
- •
- Przypisania pamięci oznaczone znacznikiem MADV_DONTFORK
madvise(2), nie są dziedziczone poprzez fork().
- •
- Pamięć w przedziałach adresowych oznaczonych
znacznikiem MADV_WIPEONFORK madvise(2) jest zerowana dla
procesu potomnego, po wykonaniu fork() (ustawienie
MADV_WIPEONFORK nie ulega zmianie dla tych
przedziałów adresowych, dla procesu potomnego).
- •
- Sygnałem przerwania procesu potomnego jest zawsze SIGCHLD
(zob. clone(2)).
- •
- Bity uprawnień dostępu do portu ustawione za pomocą
ioperm(2) nie są dziedziczone przez proces potomny; musi on
sam włączyć wszystkie wymagane bity przy
użyciu ioperm(2).
Dalsze uwagi:
- •
- Proces potomny jest tworzony jednym wątkiem — tym
który wywołał fork(). Cała wirtualna
przestrzeń adresowa jest replikowana dla procesu potomnego;
obejmuje to zatrzaski (muteksy), zmienne warunkowe i inne obiekty pthread;
podręcznik pthread_atfork(3) może okazać
się przydatny w radzeniu sobie z problemami, jakie to może
spowodować.
- •
- W programie wielowątkowym, po fork() proces potomny
może bezpiecznie wykonać jedynie funkcje które
są async-signal-safe (zob. signal-safety(7)) aż do
momentu, gdy nie wywoła execve(2).
- •
- Proces potomny dziedziczy kopie zestawu deskryptorów otwartego
pliku. Każdy deskryptor pliku procesu potomnego odnosi się
do tego samego opisu otwartego pliku (OFD, zob. open(2)) jako
deskryptor odpowiadającego pliku swego procesu macierzystego.
Oznacza to, że dwa deskryptory pliku dzielą znaczniki
statusu otwartego pliku, przesunięcia pliku oraz atrybuty
wejście/wyjścia zasilane sygnałami (zob. opis
F_SETOWN i F_SETSIG w fcntl(2)).
- •
- Proces potomny dziedziczy zestaw deskryptorów otwartej kolejki
komunikatów procesu macierzystego (zob. mq_overview(7)).
Każdy deskryptor w procesie potomnym odnosi się do tego
samego opisu kolejki otwartego komunikatu, jak odpowiadający
deskryptor pliku procesu macierzystego. Oznacza to, że dwa
deskryptory pliku dzielą te same znaczniki (mq_flags).
- •
- Proces potomny kopiuje zestaw strumieni otwartego katalogu procesu
macierzystego (zob. opendir(3)). POSIX.1 wskazuje, że
odpowiadające strumienie katalogów procesu macierzystego i
potomnego mogą dzielić pozycjonowanie strumienia
katalogu, w Linuksie/glibc tak się nie dzieje.
Po pomyślnym zakończeniu, w procesie macierzystym
zwracany jest PID procesu potomnego, a w procesie potomnym zwracane jest 0.
Po błędzie zwracane jest -1 do procesu macierzystego, nie jest
tworzony procesie potomny i odpowiednio ustawiane jest errno
wskazując błąd.
- EAGAIN
- Napotkano nałożony systemowo limit liczby
wątków. Występuje wiele limitów, które
mogą wyzwolić ten błąd:
- •
- osiągnięto miękki limit zasobów
RLIMIT_NPROC (ustawiany za pomocą setrlimit(2)),
który ogranicza liczbę procesów i
wątków dla rzeczywistego identyfikatora
użytkownika;
- •
- osiągnięto systemowy limit jądra na liczbę
procesów i wątków /proc/sys/kernel/threads-max
(zob. proc(5));
- •
- osiągnięto maksymalną liczbę
identyfikatorów procesów /proc/sys/kernel/pid_max
(zob. proc(5)); albo
- •
- osiągnięto limit identyfikatorów procesów
(pids.max) nałożony przez kontroler cgroup
„liczba procesów” (PID-ów).
- EAGAIN
- Wywołanie działa według zasad planisty
SCHED_DEADLINE i nie posiada znacznika zresetuj-przy-rozwidleniu.
Zob. sched(7).
- ENOMEM
- fork() nie potrafił zaalokować niezbędnych
struktur jądra z powodu niedostatecznej ilości
pamięci.
- ENOMEM
- Próbowano utworzyć proces potomny w przestrzeni nazw PID,
której proces „init” uległ zakończeniu.
Zob. pid_namespaces(7).
- ENOSYS
- fork() nie jest obsługiwane na tej platformie (np.
sprzęt bez jednostki Memory-Management Unit).
- ERESTARTNOINTR
(od Linuksa 2.6.17)
- Wywołanie systemowe przerwano sygnałem i zostanie ono
przeładowane (widać to tylko przy śledzeniu).
Od glibc 2.3.3, zamiast przywoływać wywołanie
systemowe jądra fork(), opakowanie fork() z biblioteki
glibc, dostępne jako część implementacji
wątkowania NPTL, przywołuje clone(2) ze znacznikami,
zapewniającymi taki sam efekt, jak tradycyjne wywołanie
systemowe (wywołanie fork() jest równoważne
wywołaniu clone(2) przy określeniu flags jako
wyłącznie SIGCHLD). Opakowanie z biblioteki glibc
przywołuje procedury obsługi rozwidlania, które
ustanowiono za pomocą pthread_atfork(3).
POSIX.1-2001, SVr4, 4.3BSD.
Pod Linuksem fork() jest zaimplementowane za pomocą
kopiowania stron pamięci przy zapisie, więc jedynymi
mankamentami są czas i pamięć wymagane do powielenia
tablic stron rodzica i utworzenia unikalnej struktury zadania dla
potomka.
Zob. pipe(2) i wait(2), aby obejrzeć
więcej przykładów.
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
pid_t pid;
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
perror("sygnał");
exit(EXIT_FAILURE);
}
pid = fork();
switch (pid) {
case -1:
perror("fork");
exit(EXIT_FAILURE);
case 0:
puts("Proces potomny wychodzi.");
exit(EXIT_SUCCESS);
default:
printf("Proces potomny ma PID %jd\n", (intmax_t) pid);
puts("Proces macierzysty wychodzi.");
exit(EXIT_SUCCESS);
}
}
clone(2), execve(2), exit(2),
setrlimit(2), unshare(2), vfork(2), wait(2),
daemon(3), pthread_atfork(3), capabilities(7),
credentials(7)
Tłumaczenie niniejszej strony podręcznika: Przemek
Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz
<ankry@green.mf.pg.gda.pl> i Michał Kułach
<michal.kulach@gmail.com>
Niniejsze tłumaczenie jest wolną
dokumentacją. Bliższe informacje o warunkach licencji
można uzyskać zapoznając się z
GNU General
Public License w wersji 3 lub nowszej. Nie przyjmuje się
ŻADNEJ ODPOWIEDZIALNOŚCI.
Błędy w tłumaczeniu strony podręcznika
prosimy zgłaszać na adres listy dyskusyjnej
manpages-pl-list@lists.sourceforge.net.