Récolter correctement tous les processus enfants et collecter le statut de sortie

Je souhaite capturer tous les processus enfants créés par un processus parent, puis collecter le statut de sortie du dernier enfant. À cette fin, j'ai appelé sigsuspend() pour attendre un signal SIGCHLD. Lorsque je reçois le signal SIGCHLD, le gestionnaire appellera waitpid dans une boucle jusqu'à ce qu'il indique qu'il ne reste plus d'enfants à récolter. L'état de sortie sera défini et le principal sortira de la boucle et se terminera.

Cependant, j'ai remarqué que ce n'est pas correct, car tous les enfants ne sont pas toujours moissonnés. Comment puis-je réparer cela?

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
volatile sig_atomic_t exit_stat;
// Signal Handler
void sigchld_handler(int sig) {
pid_t pid;
int status;
while(1) {
pid = waitpid(-1, &status, WNOHANG);
if(pid <= 0) {break;}
if(WIFEXITED(status)) {
printf("%s", "Exited correctly.");
}
else {
printf("%s", "Bad exit.");
}
}
exit_stat = status;
}
// Executing code.
int main() {
signal(SIGCHLD, sigchld_handler);

sigset_t mask_child;
sigset_t old_mask;
sigemptyset(&mask_child);
sigaddset(&mask_child, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask_child, &old_mask);

for(int i = 0; i < 5; i++) {
int child_pid = fork();
if(child_pid!= 0) {
//Perform execvp call.
char* argv[] = {"echo", "hi", NULL};
execvp(argv[0], argv);
}
}

while(!exit_stat) {
sigsuspend(&old_mask);
}

return 0;
}


Solution du problème

Transférer des commentaires légèrement modifiés dans une réponse.

L' WNOHANGoption à waitpid()signifie "revenir immédiatement s'il n'y a plus d'enfants, OU s'il reste des enfants mais qu'ils courent toujours". Si vous voulez vraiment attendre que tous les enfants sortent, omettez l' WNOHANGoption waitpid()ou utilisez simplement à la wait()place. Notez que si des tâches ont été lancées en arrière-plan, elles peuvent ne pas se terminer pendant très longtemps, voire jamais. Cela dépend également du contexte si « le dernier enfant à mourir » est le bon sur lequel rapporter. Il est possible d'imaginer des scénarios où cela ne convient pas.

Vous avez raison, dans ce cas, je voulais dire que "le dernier enfant à mourir" est le dernier enfant qui a été bifurqué. Puis-je résoudre ce problème en ajoutant une condition simple pour vérifier si le pid renvoyé de wait == le pid du dernier enfant fourchu ?

Si vous êtes intéressé par le dernier enfant du pipeline le plus récent (par exemple ls | grep … | sort … | wc, et que vous voulez attendre wc), alors vous connaissez le PID de wc, et vous pouvez utiliser waitpid(wc_pid, &status, 0)pour attendre que ce processus meure spécifiquement. Ou vous pouvez utiliser votre boucle pour collecter des corps jusqu'à ce que vous trouviez le corps wcou que vous n'obteniez plus aucun processus mort. À ce stade, vous pouvez décider d'attendre spécifiquement le wcPID, ou (mieux) utiliser waitpid()sans WNOHANG(ou utiliser wait()) jusqu'à ce qu'un processus meure - et encore une fois, vous pouvez décider s'il l'était wcou non, et si ce n'est pas le cas, répétez le WNOHANGprocessus de collecte de cadavres pour collecter tous les zombies. Répétez jusqu'à ce que vous trouviez le cadavre de wc.

Et aussi, vous avez dit que les tâches en arrière-plan peuvent ne pas se terminer pendant longtemps. Par là, voulez-vous dire que waitpid(-1, &status, 0)cela suspendra complètement tous les processus jusqu'à ce qu'un enfant soit prêt à être récolté ?

waitpid(-1, &status, 0);fera attendre le processus parent indéfiniment jusqu'à ce qu'un processus enfant meure, ou il reviendra parce qu'il n'y a plus d'enfants à attendre (ce qui indique qu'il y a eu une erreur de gestion; les enfants ne doivent pas mourir sans que le parent le sache).

Notez que l'utilisation d'une boucle "attendre n'importe quel enfant" évite de laisser des zombies autour (des enfants qui sont morts mais qui n'ont pas été attendus). C'est généralement une bonne idée. Mais capturer quand l'enfant qui vous intéresse actuellement meurt garantit que votre coquille ne traîne pas en attente quand ce n'était pas nécessaire. Vous devez donc capturer à la fois le PID et l'état de sortie des processus enfants morts.

Commentaires

Posts les plus consultés de ce blog

Erreur Symfony : "Une exception a été levée lors du rendu d'un modèle"

Détecter les appuis sur les touches fléchées en JavaScript

Une chaîne vide donne "Des erreurs ont été détectées dans les arguments de la ligne de commande, veuillez vous assurer que tous les arguments sont correctement définis"