2011-06-26 4 views
4

Le programme suivant forge un enfant, qui exécute "/ bin/sleep 10" à plusieurs reprises. Le parent installe un gestionnaire de signal pour SIGINT, qui délivre SIGINT à l'enfant. Cependant, l'envoi de SIGINT à l'enfant échoue parfois. Pourquoi est-ce et qu'est-ce qui me manque?Conditions de concurrence dans mon gestionnaire de signal POSIX

#include <errno.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <unistd.h> 

pid_t foreground_pid = 0; 

void sigint_handler(int sig) 
{ 
    printf("sigint_handler: Sending SIGINT to process %d\n", 
      foreground_pid); 

    if ((foreground_pid != 0) && kill(foreground_pid, SIGCONT) == -1) { 
     perror("sending SIGINT to forground process failed"); 
     printf("foreground_pid == %d", foreground_pid); 
     exit(EXIT_FAILURE); 
    } 

    foreground_pid = 0; 
} 

int main(int argc, const char *argv[]) 
{ 
    while (1) { 
     pid_t child_pid; 

     if ((child_pid = fork()) == -1) { 
      perror("fork failed"); 
      exit(EXIT_FAILURE); 
     } 

     if (child_pid) { /* parent */ 
      foreground_pid = child_pid; 

      printf("waiting for child (%d) to complete ...\n", child_pid); 
      fflush(stdout); 

      /* install SIGINT signal handler */ 
      struct sigaction sa; 
      struct sigaction old_handler; 
      sa.sa_handler = sigint_handler; 
      sigemptyset(&sa.sa_mask); 
      sa.sa_flags = SA_RESTART | SA_RESETHAND; 
      sigaction(SIGINT, &sa, NULL); 

      int status = 0; 

      /* wait for child to finish */ 
      if (waitpid(child_pid, &status, 0) == -1) { 
       perror("waitpid failed"); 
       exit(EXIT_FAILURE); 
      } 

      printf(" done.\n"); 
      fflush(stdout); 

     } 
     else { /* child */ 
      char * const argv[] = { "/bin/sleep", "10", NULL}; 

      if (execve(argv[0], argv, NULL) == -1) { 
       perror("execve failed"); 
       exit(EXIT_FAILURE); 
      } 

      exit(EXIT_SUCCESS); 
     } 

    } 

    return EXIT_SUCCESS; 
} 

% make && ./foo 
gcc -Wall -pedantic -std=c99 -D_POSIX_C_SOURCE=200809L foo.c -o foo 
waiting for child (4582) to complete ... 
^Csigint_handler: Sending SIGINT to process 4582 
    done. 
waiting for child (4583) to complete ... 
^Csigint_handler: Sending SIGINT to process 4583 
sending SIGINT to forground process failed: No such process 
foreground_pid == 4583 

Répondre

6

Le pilote tty effectue une SIGINT sur l'ensemble du groupe de processus lors de la saisie Ctrl + C; cela inclut le processus fils, qui se fermera en réponse car il n'a pas de gestionnaire installé. Donc, vous êtes en train de dupliquer ce qui est déjà fait, et si l'enfant est toujours là quand le parent essaie de kill() c'est quelque chose d'un crapshoot.

+0

Exactement. En haut de ma tête, une solution serait d'appeler setsid() dans votre processus fils après la fourche pour mettre l'enfant dans une nouvelle session et un nouveau groupe, ceci devrait empêcher le signal SIGINT d'arriver à l'enfant. – SlappyTheFish

+0

@SlappyTheFish: 'setpgrp()' ou 'setpgid()' sont préférés dans ce cas, mais je pense que la bonne réponse est simplement de laisser le pilote tty le gérer. – geekosaur

+0

De plus, on peut installer SIG_IGN en tant que gestionnaire de signal dans l'enfant (entre fork et execve) – kmkkmk

Questions connexes