fork(2)

NAZWA
fork - tworzy proces potomny

BIBLIOTEKA
Standardowa biblioteka C (libc, -lc)

SKŁADNIA
#include <unistd.h>

pid_t fork(void);

OPIS
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.

WARTOŚĆ ZWRACANA
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.

BŁĘDY

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).

WERSJE

  Różnice biblioteki C/jądra
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).

  Bezpieczeństwo sygnałów asynchronicznych
_Fork(3) jest wariantem fork(2), który jest bezpieczny wobec sygnałów asynchronicznych.

STANDARDY
POSIX.1-2024.

HISTORIA
4.3BSD, SVr4, POSIX.1-1988.

UWAGI
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.

PRZYKŁADY
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.");
            fflush(stdout);
            _exit(EXIT_SUCCESS);
        default:
            printf("Proces potomny ma PID %jd\n", (intmax_t) pid);
            puts("Proces macierzysty wychodzi.");
            exit(EXIT_SUCCESS);
        }
    }

ZOBACZ TAKŻE
clone(2), execve(2), exit(2), _exit(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), _Fork(3), pthread_atfork(3), capabilities(7), credentials(7)

TŁUMACZENIE
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 (https://www.gnu.org/licenses/gpl-3.0.html) 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.