wait(2) | System Calls Manual | wait(2) |
wait, waitpid, waitid - Attendre la fin d'un processus
Bibliothèque C standard (libc, -lc)
#include <sys/wait.h>
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);
/* Il s'agit de l'interface de glibc et POSIX ; consultez les
NOTES pour des informations sur les appels système bruts. */
waitid() :
Depuis la glibc 2.26 :
_XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
Pour la glibc antérieure et égale à 2.25 :
_XOPEN_SOURCE
|| /* Depuis la glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
|| /* glibc <= 2.19: */ _BSD_SOURCE
Tous ces appels système attendent qu'un des enfants du processus appelant change d'état et permettent d'obtenir des informations sur l'enfant en question. Un processus est considéré comme changeant d'état s'il termine, s'il est stoppé par un signal ou s'il est relancé par un signal. Dans le cas d'un enfant qui se termine, l'attendre permet au système de libérer les ressources qui lui étaient allouées ; si le processus n'est pas attendu, il reste en état de « zombie » (voir les NOTES plus bas).
Si un enfant a déjà changé d'état, ces appels système retournent immédiatement. Sinon, ils bloquent jusqu'à ce qu'un enfant change d'état ou qu'un gestionnaire de signal interrompe l'appel (sauf si les appels système sont relancés automatiquement par l'option SA_RESTART de sigaction(2)). Dans la suite de cette page, un enfant qui a changé d'état et qui n'a pas été attendu est appelé prêt (waitable).
L'appel système wait() suspend l'exécution du processus appelant jusqu'à ce que l'un de ses enfants se termine. L'appel wait(&status) est équivalent à :
waitpid(-1, &wstatus, 0);
L'appel système waitpid() suspend l'exécution du processus appelant jusqu'à ce qu'un enfant spécifié par l'argument pid change d'état. Par défaut, waitpid() n'attend que les enfants terminés, mais ce comportement peut être modifié par l'argument options, de la façon décrite ci-dessous.
La valeur de pid peut être l'une des suivantes :
La valeur de l'argument option options est un OU binaire entre zéro ou plus des constantes suivantes :
(Pour les options spécifiques à Linux, voir plus bas.)
Si wstatus n'est pas NULL, wait() et waitpid() stockent l'état de l'enfant dans la variable de type int pointée. Cet entier peut être évalué avec les macros suivantes (qui prennent l'entier lui-même comme argument, et pas un pointeur vers celui-ci, comme le font wait() et waitpid() !) :
L'appel système waitid(), disponible depuis Linux 2.6.9, fournit des moyens plus fins de contrôler quels changements d'états attendre.
Les arguments idtype et id sélectionnent le(s) enfant(s) à attendre, comme suit :
Les changements d'état à attendre sont indiqués par un OU binaire des attributs suivants dans le paramètre options :
Les attributs suivants peuvent également être utilisés dans options :
Si l'appel réussit, waitid() remplit les champs suivants de la structure siginfo_t pointée par infop :
Si WNOHANG est utilisé dans options et si aucun enfant n'est prêt, waitid() renvoie 0 immédiatement et l'état de la structure siginfo_t pointée par infop dépend de l'implémentation. Pour différencier (de façon portable) ce cas de celui où un des enfants était prêt, définissez à zéro le champ si_pid avant l'appel, et vérifiez que sa valeur est différente de zéro après le renvoi de l'appel.
Le premier rectificatif technique POSIX.1-2008 (2013) ajoute l'exigence que quand WNOHANG est spécifié dans options et si aucun enfant n'est prêt, waitid() doit définir à zéro les champs si_pid et si_signo de la structure. Dans Linux et d'autres implémentations qui respectent cette exigence, il n'est pas nécessaire de définir à zéro le champ si_pid avant l'appel à waitid(). Néanmoins, toutes les implémentations ne suivent pas la spécification de POSIX.1 sur ce point.
wait() : en cas de réussite, l'identifiant du processus enfant terminé est renvoyé ; en cas d'échec, la valeur de retour est -1.
waitpid() : s'il réussit, l'appel renvoie l'identifiant du processus enfant dont l'état a changé ; si WNOHANG est utilisé et si un enfant (ou plus) spécifié par pid existe, mais n'a pas encore changé d'état, la valeur renvoyée est 0. En cas d'échec, -1 est renvoyé.
waitid() : renvoie 0 s'il réussit ou si WNOHANG est utilisé et siaucun enfant spécifié dans id n'a encore changé d'état. En cas d'échec, renvoie -1.
En cas d'échec, chacun de ces appels définit errno pour préciser l'erreur.
SVr4, 4.3BSD, POSIX.1-2001.
Un enfant qui se termine mais n'a pas été attendu devient un « zombie ». Le noyau conserve des informations minimales sur le processus zombie (identifiant, code de retour, informations d'utilisation des ressources) pour permettre au parent de l'attendre plus tard et d'obtenir des informations sur l'enfant. Tant que le zombie n'est pas effacé du système par une attente, il prendra un emplacement dans la table des processus du noyau, et si cette table est remplie, il sera impossible de créer de nouveaux processus. Si un processus parent se termine, ses enfants zombies (s'il y en a) sont adoptés par init(1) (ou par le processus suppresseur (subreaper) le plus proche tel que défini par l'utilisation de l'opération PR_SET_CHILD_SUBREAPER de prctl(2)) ; init(1) les attend automatiquement pour supprimer les zombies.
POSIX.1-2001 indique que si l'action pour SIGCHLD est définie à SIG_IGN ou si l'attribut SA_NOCLDWAIT est indiqué pour SIGCHLD (voir sigaction(2)), les enfants qui se terminent ne deviennent pas des zombies et un appel à wait() ou waitpid() sera bloquant jusqu'à ce que tous les enfants soient terminés, et échouera ensuite en positionnant errno à ECHILD. (La norme POSIX originale ne décrivait pas le comportement si l'action pour SIGCHLD était SIG_IGN. Veuillez noter que même si la disposition par défaut de SIGCHLD est « ignore », la configuration explicite de la disposition de SIG_IGN entraîne un traitement différent des processus enfants zombies.)
Linux 2.6 est conforme aux préconisations de POSIX. Cependant, Linux 2.4 et les versions antérieures ne l'étaient pas. Lorsqu'un appel wait() ou waitpid() était exécuté en ignorant SIGCHLD, les appels se comportaient comme si SIGCHLD était pris en compte, c'est à dire que l'appel restait bloqué en attendant que l'enfant suivant se termine, puis renvoyait l'identifiant de processus et l'état de cet enfant.
Dans le noyau Linux, un thread ordonnancé par le noyau n'est pas différent d'un simple processus. En fait, un thread est juste un processus qui est créé à l'aide de la routine — spécifique à Linux — clone(2). Les routines portables, comme pthread_create(3), sont implémentées en appelant clone(2). Avant Linux 2.4, un thread était simplement un cas particulier de processus, et en conséquence un thread ne pouvait pas attendre les enfants d'un autre thread, même si ce dernier appartenait au même groupe de threads. Toutefois, POSIX réclame une telle fonctionnalité, et depuis Linux 2.4 un thread peut, par défaut, attendre les enfants des autres threads du même groupe.
Les options suivantes sont spécifiques à Linux et servent pour les enfants créés avec clone(2) ; elles peuvent aussi, depuis Linux 4.7, être utilisées avec waitid() :
Depuis Linux 4.7, l'attribut __WALL est automatiquement impliqué si l'enfant est suivi.
wait() est en fait une fonction de bibliothèque implémentée (dans glibc) en tant qu'appel système à wait4().
sur certaines architectures, il n'y a pas d'appel système waitpid() ; à la place, cette interface est implémentée au moyen d'une fonction d'enveloppe de la bibliothèque C qui appelle wait4(2).
L'appel système brut waitid() prend un cinquième paramètre, de type struct rusage *. Si ce paramètre n'est pas NULL, il est utilisé pour renvoyer les informations d'utilisation des ressources au sujet du thread enfant, de la même façon que wait4(2). Consultez getrusage(2) pour plus de détails.
Selon la norme POSIX.1-2008, une application appelant waitid() doit garantir que infop pointe sur une structure siginfo_t (c'est-à-dire qu'elle ne pointe pas sur NULL). Sur Linux, si infop est NULL, waitid() réussit, et renvoie l'identificateur du processus enfant attendu. Dans la mesure du possible, les applications doivent éviter d'avoir recours à cette fonctionnalité incohérente, non standard et superflue.
Le programme suivant montre l'utilisation de fork(2) et de waitpid(). Le programme crée un processus enfant. Si aucun argument n'est fourni dans la ligne de commande du programme, l'enfant suspend son exécution avec pause(2), pour que l'utilisateur puisse lui envoyer des signaux. Sinon, l'enfant se termine immédiatement en utilisant l'entier fourni sur la ligne de commande comme code de retour. Le processus père boucle en surveillant l'état de l'enfant avec waitpid() et utilise les macros W*() décrites ci-dessus pour analyser le code d'état de l'enfant.
La session interactive suivante montre l'utilisation de ce programme :
$ ./a.out & Le PID de l'enfant est 32360 [1] 32359 $ kill -STOP 32360 arrêté par le signal 19 $ kill -CONT 32360 relancé $ kill -TERM 32360 tué par le signal 15 [1]+ Done ./a.out $
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> 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) { /* Code exécuté par l'enfant */
printf("Le PID de l'enfant est %jd\n", (intmax_t) getpid());
if (argc == 1)
pause(); /* Attendre un signal */
_exit(atoi(argv[1]));
} else { /* Code exécuté par le parant */
do {
w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(wstatus)) {
printf("terminé, code=%d\n", WEXITSTATUS(wstatus));
} else if (WIFSIGNALED(wstatus)) {
printf("tué par le signal %d\n", WTERMSIG(wstatus));
} else if (WIFSTOPPED(status)) {
printf("arrêté par le signal %d\en", WSTOPSIG(wstatus));
} else if (WIFCONTINUED(wstatus)) {
printf("relancé\n");
}
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
exit(EXIT_SUCCESS);
} }
_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)
La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org>, Cédric Boutillier <cedric.boutillier@gmail.com>, Frédéric Hantrais <fhantrais@gmail.com> et Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>
Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.
5 février 2023 | Pages du manuel de Linux 6.03 |