intercepter des signaux en lisant un tuyau avec select ()

Utiliser select () avec pipe – C’est ce que je fais et je dois maintenant attraper SIGTERM à ce sujet. Comment puis-je le faire? Dois-je le faire lorsque select() renvoie une erreur (<0)?

Tout d’abord, SIGTERM va tuer votre processus s’il n’est pas attrapé, et select() ne retournera pas . Ainsi, vous devez installer un gestionnaire de signal pour SIGTERM . Faites cela en utilisant sigaction() .

Cependant, le signal SIGTERM peut arriver à un moment où votre thread n’est pas bloqué à select() . Ce serait une condition rare, si votre processus dormait principalement sur les descripteurs de fichier, mais cela peut arriver. Cela signifie que votre gestionnaire de signal doit faire quelque chose pour informer la routine principale de l’interruption, en définissant une variable de drapeau (de type sig_atomic_t ), ou vous devez garantir que SIGTERM n’est délivré que lorsque le processus dort sur select() .

Je vais opter pour la dernière approche, car elle est plus simple, mais moins flexible (voir fin du post).

Donc, vous bloquez SIGTERM juste avant d’appeler select() et vous le rebloquez immédiatement après le retour de la fonction, de sorte que votre processus ne reçoive le signal que pendant qu’il dort dans select() . Mais notez que cela crée en fait une condition de concurrence Si le signal arrive juste après le déblocage, mais juste avant l’appel de select() , l’appel système n’aura pas encore été appelé et il ne retournera donc pas -1 . Si le signal arrive juste après que select() retourne avec succès, mais juste avant le reblocage, vous avez également perdu le signal.

Ainsi, vous devez utiliser pselect() pour cela. Il fait le blocage / déblocage autour de select() atomique.

Commencez par bloquer SIGTERM aide de sigprocmask() avant d’entrer dans la boucle pselect() . Ensuite, appelez simplement pselect() avec le masque original renvoyé par sigprocmask() . De cette façon, vous garantissez que votre processus ne sera interrompu que pendant le sumil de select() .

En résumé:

  1. Installez un gestionnaire pour SIGTERM (qui ne fait rien);
  2. Avant d’entrer dans la boucle pselect() , bloquez SIGTERM aide de sigprocmask() ;
  3. Appelez pselect() avec l’ancien masque de signal renvoyé par sigprocmask() ;
  4. Dans la boucle pselect() , vous pouvez maintenant vérifier en toute sécurité si pselect() renvoyé -1 et errno est EINTR .

Veuillez noter que si, après le pselect() de pselect() , vous effectuez beaucoup de travail, vous risquez de rencontrer une latence plus importante lorsque vous SIGTERM à SIGTERM (car le processus doit traiter et retourner à pselect() avant de traiter le signal). Si cela pose un problème, vous devez utiliser une variable d’indicateur à l’intérieur du gestionnaire de signaux afin de pouvoir rechercher cette variable dans un certain nombre de points spécifiques de votre code. L’utilisation d’une variable drapeau n’élimine pas la condition de pselect() et n’élimine pas la nécessité de pselect() .

Rappelez-vous: chaque fois que vous devez attendre certains descripteurs de fichier ou recevoir un signal, vous devez utiliser pselect() (ou ppoll() , pour les systèmes qui le prennent en charge).

Edit: rien de mieux qu’un exemple de code pour illustrer l’utilisation.

 #define _POSIX_C_SOURCE 200809L #include  #include  #include  #include  #include  #include  // Signal handler to catch SIGTERM. void sigterm(int signo) { (void)signo; } int main(void) { // Install the signal handler for SIGTERM. struct sigaction s; s.sa_handler = sigterm; sigemptyset(&s.sa_mask); s.sa_flags = 0; sigaction(SIGTERM, &s, NULL); // Block SIGTERM. sigset_t sigset, oldset; sigemptyset(&sigset); sigaddset(&sigset, SIGTERM); sigprocmask(SIG_BLOCK, &sigset, &oldset); // Enter the pselect() loop, using the original mask as argument. fd_set set; FD_ZERO(&set); FD_SET(0, &set); while (pselect(1, &set, NULL, NULL, NULL, &oldset) >= 0) { // Do some processing. Note that the process will not be // interrupted while inside this loop. sleep(5); } // See why pselect() has failed. if (errno == EINTR) puts("Interrupted by SIGTERM."); else perror("pselect()"); return EXIT_SUCCESS; } 

La réponse se trouve en partie dans l’un des commentaires de la question-réponse que vous avez indiquée;

> Une interruption fera que select () renvoie un -1 avec errno contenant le code d’erreur EINTR

C’est; pour toute interruption (signal) capturée, la sélection revient et l’errno est réglé sur EINTR.

Maintenant, si vous voulez spécifiquement attraper SIGTERM, vous devez le configurer avec un appel à signal , comme ceci;

 signal(SIGTERM,yourcatchfunction); 

où votre fonction de capture devrait être définie quelque chose comme

 void yourcatchfunction(int signaleNumber) { .... } 

Donc, en résumé, vous avez configuré un gestionnaire de signal yourcatchfunction et votre programme est actuellement dans un appel select() attente d’IO – quand un signal arrive, votre catchfunction sera appelée et lorsque vous reviendrez de cet appel, l’appel sélectionné reviendra avec le errno réglé sur EINTR.

Toutefois, sachez que le SIGTERM peut survenir à tout moment . Vous risquez donc de ne pas participer à l’appel select. Dans ce cas, vous ne verrez jamais le numéro EINTR, mais uniquement un appel régulier de la fonction yourcatchfunction

Par conséquent, select () renvoyant avec err et errno EINTR sert uniquement à permettre une action non bloquante – ce n’est pas ce qui capturera le signal.

Vous pouvez appeler select() en boucle. Ceci est appelé redémarrage de l’appel système. Voici quelques pseudo-C.

 int retval = -1; int select_errno = 0; do { retval = select(...); if (retval < 0) { /* Cache the value of errno in case a system call is later * added prior to the loop guard (ie, the while expression). */ select_errno = errno; } /* Other system calls might be added here. These could change the * value of errno, losing track of the error during the select(), * again this is the reason we cached the value. (Eg, you might call * a log method which calls gettimeofday().) */ /* Automatically restart the system call if it was interrupted by * a signal -- with a while loop. */ } while ((retval < 0) && (select_errno == EINTR)); if (retval < 0) { /* Handle other errors here. See select man page. */ } else { /* Successful invocation of select(). */ }