wait(2) System Calls Manual wait(2) NAZWA wait, waitpid, waitid - oczekuje na zmiane stanu procesu BIBLIOTEKA Standardowa biblioteka C (libc, -lc) SKLADNIA #include pid_t wait(int *_Nullable wstatus); pid_t waitpid(pid_t pid, int *_Nullable wstatus, int options); int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); /* Jest to interfejs glibc i POSIX; zob. UWAGI, aby uzyskac informacje o surowym wywolaniu systemowym. */ Wymagane ustawienia makr biblioteki glibc (patrz feature_test_macros(7)): waitid(): Od glibc 2.26: _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L glibc 2.25 i wczesniejsze: _XOPEN_SOURCE || /* Od glibc 2.12: */ _POSIX_C_SOURCE >= 200809L || /* glibc <= 2.19: */ _BSD_SOURCE OPIS Wszystkie te wywolania systemowe sluza do oczekiwania na zmiane statusu potomka procesu wywolujacego oraz do uzyskania informacji o potomku, ktorego stan ulegl zmianie. Za zmiane stanu uwaza sie: zakonczenie potomka, zatrzymanie potomka przez sygnal, wznowienie potomka przez sygnal. W przypadku zakonczonego potomka, wykonanie oczekiwania pozwala systemowi na zwolnienie zasobow zwiazanych z potomkiem; gdyby oczekiwanie nie zostalo wykonane, to potomek pozostalby w stanie ,,zombie" (zob. UWAGI ponizej). Jesli stan potomka juz ulegl zmianie, to te wywolania niezwlocznie powroca. W przeciwnym przypadku zablokuja, do momentu zmiany stanu potomka lub przerwania wywolania przez procedure obslugi sygnalu (zakladajac, ze wywolania systemowe nie sa automatycznie uruchamiane ponownie ze wzgledu na znacznik SA_RESTART sigaction(2)). W pozostalej czesci niniejszego podrecznika, potomek, ktorego stan ulegl zmianie, ale na ktorego jeszcze nie poczekano za pomoca opisywanych wywolan systemowych, bedzie okreslany terminem oczekiwalnego. wait() i waitpid() Wywolanie systemowe wait() wstrzymuje wykonanie watku wywolujacego, dopoki nie zakonczy sie jeden z jego potomkow. Wywolanie wait(&wstatus) jest rownowazne: waitpid(-1, &wstatus, 0); Wywolanie systemowe waitpid() wstrzymuje wykonanie watku wywolujacego, dopoki potomek okreslony argumentem pid nie zmieni stanu. Domyslnie, waitpid() czeka jedynie na zatrzymanie potomka, lecz to zachowanie mozna zmienic argumentem options, jak opisano nizej. Wartosc pid moze wynosic: < -1 oznacza oczekiwanie na dowolnego potomka, ktorego identyfikator grupy procesow jest rowny modulowi wartosci (wartosci bezwzglednej) pid. -1 oznacza oczekiwanie na dowolnego potomka. 0 oznacza oczekiwanie na dowolnego potomka, ktorego identyfikator grupy procesow jest rowny identyfikatorowi procesu wolajacego, w momencie wywolania waitpid(). > 0 oznacza oczekiwanie na potomka, ktorego PID jest rowny wartosci pid. Wartosc options jest suma (OR) zera lub wiecej sposrod nastepujacych stalych: WNOHANG oznacza natychmiastowy powrot z funkcji, jesli potomek nie zakonczyl pracy. WUNTRACED powraca rowniez, jesli potomek zostal zatrzymany (lecz nie sledzony za pomoca ptrace(2)). Status potomkow sledzonych, ktore sa zatrzymane jest zapewniany nawet gdy nie podano tej opcji. WCONTINUED (od Linuksa 2.6.10) powraca rowniez, jesli zatrzymany potomek zostal wznowiony sygnalem SIGCONT. (Ponizsze opcje dotycza wylacznie Linuksa.) Jesli wstatus nie wynosi NULL, to wait() i waitpid() przechowuja informacje o statusie w int, na ktora on wskazuje. Liczbe te mozna sprawdzic nastepujacymi makrami (ktore jako argument przyjmuja nie wskaznik do niej, jak czynia to wait() i waitpid(), lecz sama liczbe!): WIFEXITED(wstatus) zwraca prawde, jesli potomek zakonczyl sie w sposob normalny tj. wywolujac exit(3) lub _exit(2) albo powracajac z main(). WEXITSTATUS(wstatus) zwraca status zakonczenia potomka. Sklada sie on z osmiu najmniej znaczacych bitow argumentu status, ktore potomek podal w wywolaniu do exit(3) lub _exit(2) albo argumentu powrotnego z main(). Makro to moze byc przetwarzane tylko jesli WIFEXITED zwrocilo prawde. WIFSIGNALED(wstatus) zwraca prawde, jesli potomek zostal zakonczony sygnalem. WTERMSIG(wstatus) zwraca numer sygnalu, ktory spowodowal zakonczenie procesu potomnego. Makro to powinno byc uzywane tylko po zwroceniu prawdy przez WIFSIGNALED. WIFSTOPPED(wstatus) zwraca prawde, jesli potomek dokonal zrzutu pamieci (zob. core(5)). Makra tego nalezy uzywac tylko wowczas, gdy WIFSIGNALED zwrocilo prawde. Makro nie jest okreslone w POSIX.1-2001 i nie jest dostepne w niektorych implementacjach Uniksa (np. AIX, SunOS). Nalezy go zatem uzywac wewnatrz #ifdef WCOREDUMP ... #endif. WIFSTOPPED(wstatus) zwraca prawde, jesli proces potomny zostal zatrzymany po dostarczeniu sygnalu; jest to mozliwe tylko jesli wywolanie wykonano z uzyciem WUNTRACED lub gdy potomek jest sledzony (zob. ptrace(2)). WSTOPSIG(wstatus) zwraca numer sygnalu, ktory spowodowal zatrzymanie potomka. Makro to moze byc uzyte tylko, gdy WIFSTOPPED zwrocilo prawde. WIFCONTINUED(wstatus) (od Linuksa 2.6.10) zwraca prawde, jesli proces potomny zostal wznowiony, ze wzgledu na otrzymanie SIGCONT. waitid() Wywolanie systemowe waitid() (dostepne od Linuksa 2.6.9) udostepnia precyzyjniejsza kontrole nad stanem potomka, na ktorego sie czeka. Argumenty idtype i id wybieraja potomka (potomkow), na ktorego sie oczekuje, jak ponizej: idtype == P_PID Oczekuje na potomka, ktorego PID jest rowny id. idtype == P_PIDFD (od Linuksa 5.4) Oczekuje na potomka, do ktorego odnosi sie deskryptor pliku PID podany w id (wiecej informacji o deskryptorach pliku PID w podreczniku pidfd_open(2)). idtype == P_PGID Oczekuje na dowolnego potomka, ktorego identyfikator grupy jest rowny id. Od Linuksa 5.4, jesli id wynosi zero, to oczekuje na dowolnego potomka z tej samej grupy procesow, co grupa procesu wywolujacego w momencie wywolania. idtype == P_ALL Oczekuje na dowolnego potomka; id jest ignorowane. Zmiane statusu potomka, na ktorego sie oczekuje, podaje sie za pomoca sumy (OR) jednego lub kilku ponizszych znacznikow w options: WEXITED Oczekuje na potomka, ktory zostal zakonczony. WSTOPPED Oczekuje na potomka, ktory zostal zakonczony za pomoca dostarczenia sygnalu. WCONTINUED Oczekuje na (poprzednio zatrzymanego) potomka, ktory zostal wznowiony sygnalem SIGCONT. Ponadto, w options mozna zsumowac ponizsze znaczniki: WNOHANG Tak jak w waitpid(). WNOWAIT Pozostawia potomka w stanie oczekiwalnym; pozniejszym wywolaniem oczekiwania mozna nastepnie pobrac informacje o stanie potomka. Po pomyslnym powrocie, waitid() uzupelnia nastepujace pola struktury siginfo_t, na ktora wskazuje infop: si_pid Identyfikator procesu potomnego. si_uid Rzeczywisty identyfikator uzytkownika potomka (pole to nie jest ustawione w wiekszosci innych implementacji). si_signo Zawsze ustawione na SIGCHLD. si_status Status zakonczenia potomka, taki jak podany do _exit(2) (lub exit(3)) lub sygnal powodujacy zakonczenie, zatrzymanie lub kontynuowanie potomka. Pole si_code moze posluzyc do okreslenia sposobu interpretacji tego pola. si_code Ustawione na jeden z: CLD_EXITED (potomek wywolal _exit(2)); CLD_KILLED (potomek zabity sygnalem); CLD_DUMPED (potomek zabity sygnalem, zrzucil pamiec); CLD_STOPPED (potomek zatrzymany sygnalem); CLD_TRAPPED (sledzony potomek wyzwolil pulapke) lub CLD_CONTINUED (potomek kontynuowal po otrzymaniu SIGCONT). Jesli w options podano WNOHANG i nie bylo potomkow w stanie oczekiwalnym, to waitid() zwroci bezzwlocznie 0, natomiast stan struktury siginfo_t, na ktora wskazuje infop zalezy od implementacji. Aby (w sposob przenosny) rozroznic ten przypadek od sytuacji, gdy potomek byl w stanie oczekiwalnym, nalezy wyzerowac pole si_pid przed wywolaniem i sprawdzic, czy wartosc pola jest niezerowa, po powrocie wywolania. POSIX.1-2008 Technical Corrigendum 1 (2013) dodaje wymaganie, ze gdy w options podano WNOHANG i nie bylo potomkow w stanie oczekiwalnym, to waitid() powinno wyzerowac pola si_pid i si_signo struktury. W Linuksie i innych implementacjach, ktore przestrzegaja tego wymagania, nie ma potrzeby zerowania pola si_pid, przed wywolaniem waitid(). Obecnie, nie wszystkie implementacje przestrzegaja jednak tego wymagania POSIX.1. WARTOSC ZWRACANA wait(): po pomyslnym zakonczeniu zwraca identyfikator procesu zakonczonego potomka, w razie bledu zwraca -1. waitpid(): po pomyslnym zakonczeniu, zwraca identyfikator procesu, ktorego stan ulegl zmianie; jesli podano WNOHANG i jeden lub wiecej z potomkow podanych w pid istnieje, lecz nie zmienil jeszcze stanu, zwracane jest 0. W przypadku niepowodzenia, zwracane jest -1. waitid(): zwraca 0 po pomyslnym zakonczeniu lub gdy podano WNOHANG i zaden z potomkow podanych w id nie zmienil jeszcze stanu; w przypadku bledu zwracane jest -1. W przypadku niepowodzenia, wszystkie wywolania ustawiaja errno, wskazujac blad. BLEDY EAGAIN Deskryptor pliku PID podany w id jest nieblokujacy, a proces do ktorego sie odnosi nie zakonczyl sie. ECHILD (dla wait()) Proces wywolujacy nie ma zadnych potomkow, na ktorych jeszcze sie nie czeka. ECHILD (dla waitpid() lub waitid()) Proces o zadanym pid (waitpid()) lub idtype i id (waitid()) nie istnieje lub nie jest potomkiem procesu wywolujacego (moze sie to zdarzyc rowniez w przypadku potomka, ktory ustawil akcje obslugi sygnalu SIGCHLD na SIG_IGN; zob. takze: watki w rozdziale Uwagi linuksowe). EINTR Nie ustawiono WNOHANG, a zostal przechwycony niezablokowany sygnal lub SIGCHLD; zob. signal(7). EINVAL Argument options byl niepoprawny. ESRCH (dla wait() lub waitpid()) pid jest rowne INT_MIN. WERSJE Roznice biblioteki C/jadra wait() jest w rzeczywistoscia funkcja biblioteczna, ktora (w glibc) jest zaimplementowana jako wywolanie do wait4(2). Na niektorych architekturach, nie ma wywolania systemowego waitpid(); zamiast tego interfejs ten jest zaimplementowany jako opakowujaca funkcja biblioteki C, ktora wywoluje wait4(2). Surowe wywolanie systemowe waitid() przyjmuje piaty argument, typu struct rusage *. Jesli argument ten nie wynosi NULL, to jest uzywany do zwrocenia informacji o uzyciu zasobow potomka, w ten sam sposob jak wait4(2). Wiecej szczegolow w podreczniku getrusage(2). STANDARDY POSIX.1-2008. HISTORIA SVr4, 4.3BSD, POSIX.1-2001. UWAGI Potomek ktory sie zakonczy, ale na ktorego nie oczekiwano staje sie ,,zombie". Jadro przechowuje minimalny zbior informacji o procesach zombie (PID, status zakonczenia, informacje o uzyciu zasobow), aby pozwolic pozniej procesowi macierzystemu na wykonanie oczekiwania, aby pozyskac informacje o potomku. Dopoki zombie nie zostanie usuniety z systemu poprzez oczekiwanie, bedzie zajmowal miejsce w tablicy procesow jadra, a jesli ta tablica sie wypelni, nie bedzie mozna tworzyc nowych procesow. Jesli proces macierzysty zakonczy sie, to ewentualni potomkowie ,,zombie", zostana zaadoptowani przez init(1), (lub przez najblizszy proces dorzynajacy (ang. subreaper), wedlug definicji uzycia operacji PR_SET_CHILD_SUBREAPER prctl(2)); init(1) automatycznie wykona odczekanie, w celu usuniecia zombie. POSIX.1-2001 okresla, ze jesli jako dyspozycje sygnalu SIGCHLD ustawiono na SIG_IGN lub dla SIGCHLD ustawiono znacznik SA_NOCLDWAIT (zob. sigaction(2)), to konczony potomek nie staje sie zombie, a wywolanie wait() lub waitpid() zablokuje, dopoki wszyscy potomkowie nie zakoncza sie, a nastepnie zawiedzie z errno ustawionym na ECHILD. (Pierwotny standard POSIX pozostawial zachowanie ustawienia SIGCHLD na SIG_IGN nieokreslonym. Prosze zauwazyc, ze choc domyslna dyspozycja SIGCHLD jest ,,ignorowanie", to wyrazne ustawienie dyspozycji na SIG_IGN daje inne zachowanie w stosunku do potomkow procesu zombie). Linux 2.6 jest zgodny z wymaganiami POSIX. Jednak Linux 2.4 (i wczesniejsze) nie sa: jesli wywolanie wait() lub waitpid() jest wykonywane z ignorowaniem SIGCHLD, zachowuje sie ono tak, jakby SIGCHLD nie byly ignorowane, to znaczy, wywolanie zostaje zablokowane do chwili zakonczenia nastepnego potomka, a nastepnie zwraca identyfikator procesu i status tego potomka. Uwagi linuksowe Pod Linuksem, watek zarzadzany przez jadro nie jest uruchamiany inaczej niz zwykly proces. Zamiast tego watek jest po prostu procesem stworzonym przez wywolanie dostepnej tylko pod Linuksem funkcji systemowej clone(2). Inne funkcje, jak na przyklad przenosne pthread_create(3) sa zaimplementowane przez wywolania funkcji clone(2). W wersjach Linuksa poprzedzajacych 2.4, watek byl po prostu specyficznym przypadkiem procesu. W zwiazku z tym nie mogl on czekac na potomkow innego watku nawet w przypadku, gdy ten drugi watek nalezal do tej samej grupy watkow. Jednakze, POSIX zaleca taka funkcjonalnosc, wiec poczawszy od Linuksa 2.4 watek moze (i domyslnie bedzie) czekac na potomkow innych watkow nalezacych do tej samej grupy watkow. Nastepujace, specyficzne dla Linuksa opcje w options sa przeznaczone dla potomkow utworzonych za pomoca clone(2); moga byc one rowniez, od Linuksa 4.7, uzywane z waitid(): __WCLONE Oczekuje tylko na potomkow typu ,,clone". Jesli opcja ta zostanie pominieta oczekiwanie bedzie wystepowac tylko na potomkow typu ,,nie-clone". (Potomek typu ,,clone" to taki, ktory po zakonczeniu nie dostarcza swojemu procesowi macierzystemu sygnalu lub dostarcza sygnal inny niz SIGCHLD.) Opcja ta jest ignorowana, jesli ustawiona jest rowniez opcja __WALL. __WALL (od Linuksa 2.4) Oczekuje na procesy potomne niezaleznie od ich typu (,,clone" lub ,,non-clone"). __WNOTHREAD (od Linuksa 2.4) Nie oczekuje na procesy potomne innych watkow w obrebie tej samej grupy watkow. Bylo to w Linuksie domyslne przed wersja 2.4. Od Linuksa 4.7, znacznik __WALL jest automatycznie dorozumiany, gdy potomek jest sledzony (ptraced). USTERKI Zgodnie z POSIX.1-2008, aplikacje wywolujace waitid() musza upewnic sie, ze infop wskazuje na strukture siginfo_t (tj. jest nie to pusty wskaznik). W Linuksie, jesli infop wynosi NULL, to waitid() konczy sie powodzeniem, zwracajac identyfikator procesu oczekiwanego potomka. Aplikacje powinny unikac polegania na tym niespojnym, niestandardowym i niepotrzebnym zachowaniu. PRZYKLADY Ponizszy program demonstruje uzycie fork(2) i waitpid(). Program tworzy proces potomny. Jesli nie poda sie argumentow wiersza polecen, potomek zawiesza swoje wykonanie za pomoca pause(2), aby pozwolic uzytkownikowi wysylac sygnaly do potomka. W przeciwnym przypadku, gdy poda sie argumenty wiersza polecen, potomek bezzwlocznie wychodzi, uzywajac liczby podanej w wierszu polecenia jako statusu zakonczenia. Proces macierzysty wykonuje petle monitorujaca potomka za pomoca waitpid() i uzywa makr W*() opisanych powyzej, do analizy wartosci statusu oczekiwania. Ponizsza sesja powloki demonstruje uzycie programu: $ ./a.out & PID potomka to 32360 [1] 32359 $ kill -STOP 32360 zatrzymany sygnalem 19 $ kill -CONT 32360 wznowiony $ kill -TERM 32360 zabity sygnalem 15 [1]+ Zakonczony ./a.out $ Kod zrodlowy programu #include #include #include #include #include #include int main(int argc, char *argv[]) { int wstatus; pid_t cpid, w; cpid = fork(); if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (cpid == 0) { /* Kod wykonywany przez potomka */ printf("PID potomka to %jd\n", (intmax_t) getpid()); if (argc == 1) pause(); /* Oczekiwanie na sygnaly */ _exit(atoi(argv[1])); } else { /* Kod wykonywany przez rodzica */ do { w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED); if (w == -1) { perror("waitpid"); exit(EXIT_FAILURE); } if (WIFEXITED(wstatus)) { printf("wyszedl, status=%d\n", WEXITSTATUS(wstatus)); } else if (WIFSIGNALED(wstatus)) { printf("zabity sygnalem %d\n", WTERMSIG(wstatus)); } else if (WIFSTOPPED(wstatus)) { printf("zatrzymany sygnalem %d\n", WSTOPSIG(wstatus)); } else if (WIFCONTINUED(wstatus)) { printf("wznowiony\n"); } } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); exit(EXIT_SUCCESS); } } ZOBACZ TAKZE _exit(2), clone(2), fork(2), kill(2), ptrace(2), sigaction(2), signal(2), wait4(2), pthread_create(3), core(5), credentials(7), signal(7) TLUMACZENIE Autorami polskiego tlumaczenia niniejszej strony podrecznika sa: Przemek Borys , Andrzej Krzysztofowicz i Michal Kulach Niniejsze tlumaczenie jest wolna dokumentacja. Blizsze informacje o warunkach licencji mozna uzyskac zapoznajac sie z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje sie ZADNEJ ODPOWIEDZIALNOSCI. Bledy w tlumaczeniu strony podrecznika prosimy zglaszac na adres listy dyskusyjnej . Linux man-pages 6.8 2 maja 2024 r. wait(2)