Dois-je faire quelque chose avec un gestionnaire SIGCHLD si j’utilise seulement wait () pour attendre qu’un enfant termine à la fois?

J’ai un programme qui oblige un enfant à travailler, mais je ne fais qu’un enfant à la fois à la fois. J’utilise wait() pour attendre que l’enfant ait terminé. Dois-je également faire quelque chose avec SIGCHLD (comme désactiver le gestionnaire)?

Dans ma situation, je reçois une valeur EINTR in errno qui me porte à penser que je dois masquer SIGCHLD .

En gros, voici le programme:

  • lire les arguments
  • pour (liste de travail à faire)
  • fork()
  • si enfant, execlp() au programme de travail
  • si parent, wait() que l’enfant termine
  • lorsque l’enfant a terminé, le parent passe en boucle au prochain élément de travail

Théorie

POSIX dit à propos de SIG_IGN (ssortingctement sous la note d’extension XSI):

Si l’action pour le signal SIGCHLD est définie sur SIG_IGN, les processus enfants des processus appelants ne doivent pas être transformés en processus zombie lorsqu’ils se terminent. Si, par la suite, le processus d’appel attend ses enfants et qu’il ne compte aucun enfant inattendu transformé en processus zombie, il bloquera jusqu’à ce que tous ses enfants soient terminés et wait (), waitid () et waitpid () échec et mettre errno à [ECHILD].

Et dans la description de indique que la disposition du signal par défaut pour SIGCHLD est SIG_IGN ‘ignore’ (code ‘I’). Cependant, le comportement de SIG_DFL consiste à “ignorer” le signal; le signal n’est jamais généré. Ceci est différent du comportement SIG_IGN .

Donc, vous n’avez pas besoin de définir un gestionnaire de signal, mais vous ne devriez pas obtenir d’informations sur le processus, vous devriez juste avoir une erreur une fois qu’il ne rest plus d’enfants.

Code de démonstration

 #include  #include  #include  #include  #include  #include  static siginfo_t sig_info; static volatile sig_atomic_t sig_num; static void *sig_ctxt; static void catcher(int signum, siginfo_t *info, void *vp) { sig_num = signum; sig_info = *info; sig_ctxt = vp; } static void set_handler(int signum) { struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = catcher; sigemptyset(&sa.sa_mask); if (sigaction(signum, &sa, 0) != 0) { int errnum = errno; fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum)); exit(1); } } static void prt_interrupt(FILE *fp) { if (sig_num != 0) { fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid); sig_num = 0; } } static void five_kids(void) { for (int i = 0; i < 5; i++) { pid_t pid = fork(); if (pid < 0) break; else if (pid == 0) { printf("PID %d - exiting with status %d\n", (int)getpid(), i); exit(i); } else { int status = 0; pid_t corpse = wait(&status); printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF)); prt_interrupt(stdout); fflush(0); } } } int main(void) { printf("SIGCHLD set to SIG_IGN\n"); signal(SIGCHLD, SIG_IGN); five_kids(); printf("SIGCHLD set to catcher()\n"); set_handler(SIGCHLD); five_kids(); return(0); } 

Le fflush(0); call garantit que la sortie standard (en particulier) est vidée, ce qui compte si la sortie du programme exemple est redirigée vers un autre programme.

Exemple de sortie

La sortie de l'exemple de code est en accord avec la théorie - mais nécessite un peu d'interprétation.

 SIGCHLD set to SIG_IGN PID 4186 - exiting with status 0 SIGCHLD set to SIG_IGN Child: 4186; Corpse: -1; Status = 0x0000 PID 4187 - exiting with status 1 Child: 4187; Corpse: -1; Status = 0x0000 PID 4188 - exiting with status 2 Child: 4188; Corpse: -1; Status = 0x0000 PID 4189 - exiting with status 3 Child: 4189; Corpse: -1; Status = 0x0000 PID 4190 - exiting with status 4 Child: 4190; Corpse: -1; Status = 0x0000 SIGCHLD set to catcher() PID 4191 - exiting with status 0 SIGCHLD set to catcher() Child: 4191; Corpse: -1; Status = 0x0000 Signal 20 from PID 4191 Child: 4192; Corpse: 4191; Status = 0x0000 PID 4192 - exiting with status 1 Child: 4193; Corpse: 4192; Status = 0x0100 Signal 20 from PID 4192 PID 4193 - exiting with status 2 Child: 4194; Corpse: 4193; Status = 0x0200 Signal 20 from PID 4193 PID 4194 - exiting with status 3 Child: 4195; Corpse: 4194; Status = 0x0300 Signal 20 from PID 4194 PID 4195 - exiting with status 4 

La première partie du résultat est en parfait accord avec la théorie. le code d'appel ne donne aucune information sur les enfants morts, sauf qu'il n'y a pas d'enfants morts (à gauche) à attendre.

La deuxième section de la sortie est également conforme à la théorie, mais il apparaît que les enfants ne s'exécutent pas avant le parent. Le premier wait() du parent n'a pas d'enfant mort à attendre et renvoie -1. Les appels suivants récupèrent les différents enfants, mais l'un d'eux s'échappe inopinément (mais le corps sera nettoyé par le système). Si le code utilisé waitpid() tel que: pid_t corpse = waitpid(pid, &status, 0); , il attendrait chaque enfant à son tour.

Certains de ces commentaires peuvent nécessiter une révision à la lumière des commentaires modifiés sur «ignorer» ci-dessus. À déterminer - manque de temps maintenant.

Mac OS X 10.8.4 sur 3 GHz Intel Core 2 Duo, GCC 4.6.0, compilation 64 bits:

 gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wssortingct-prototypes \ -Wold-style-definition sigchld.c -o sigchld 

Il s’agissait d’une adaptation rapide d’un code existant pour tester le comportement de SIGINT et de pause() . Il peut contenir des informations superflues. Vous n'êtes pas obligé d'appeler wait() ou l'un de ses parents dans le gestionnaire de signaux; vous pouvez le faire si vous le souhaitez.

Si vous ne vous souciez pas du statut de sortie des enfants forkés, vous pouvez alors ignorer SIGCHLD .

 signal(SIGCHLD, SIG_IGN); 

Si vous vous souciez de l’état de sortie, vous pouvez installer un gestionnaire de signaux et effectuer un appel à waitpid() dans le gestionnaire de signaux afin de récupérer les valeurs d’état de sortie des processus enfants sortis. Cet appel devrait être en boucle, jusqu’à ce qu’il n’y ait plus d’enfants excités. Vous utiliseriez -1 dans le premier paramètre et WNOHANG dans le dernier paramètre pour attendre tout enfant de manière non bloquante. Si waitpid() renvoie 0, cela signifie que l’appel a abouti, mais qu’il n’y a plus d’enfants à attendre.

 void sigchld_handler (int sig) { assert(sig == SIGCHLD); int status; pid_t child; while ((child = waitpid(-1, &status, WNOHANG)) > 0) { /*...do something with exited child's status */ } } /*...*/ signal(SIGCHLD, sigchld_handler);