Обработчик сигнала для завершения основной программы и всех вилок, когда прибывает двойной SIGINT

Я действительно не понял, как работают обработчики сигналов, особенно с вилками. Мне нужно сделать это упражнение, но я не смог заставить его работать должным образом.

Моя основная программа делает 5 вилок, каждая вилка печатает просто 10 сообщений со своим pid. Таким образом, цель программы, когда я посылаю сигнал SIGINT через клавиатуру (Ctrl-c), она должна вывести "прибыл один SIGINT", если два SIGINT прибывают между одной секундой, это должно вывести "двойной SIGINT прибыло" и должно завершиться вся программа. Поэтому, когда я запускаю свою программу, она обрабатывает первые два SIGINT(которые я посылаю вторым более чем через 1 секунду после первого), но затем она не обрабатывает ни один SIGINT, ни двойной SIGINT.

Так что я очень запутался в сигналах. Вилы продолжают штамповать сообщения. Я загружаю один и тот же обработчик и на основной, и на вилки, но что я должен сделать, чтобы завершить все форки, когда прибывает двойной SIGINT? Должен ли я вызвать killl или другую функцию в обработчике, чтобы прекратить их?

основная функция

 /* libraries... */

 volatile sig_atomic_t double_sigint = 0;
 int64_t time_diff = 0;

 int main()
 {
  int i;
  int pid;

  sigset_t set;
  struct sigaction sa;


         /* mask all signals */ 
 /*H*/   if(sigfillset(&set) == -1 )
 /*A*/     {perror("sigfillset"); exit(errno);} 
 /*N*/  
 /*D*/   if(sigprocmask(SIG_SETMASK,&set,NULL) == -1)
 /*L*/     {perror("sigfillset"); exit(errno);} 
 /*E*/       
 /*R*/   memset(&sa,0,sizeof(sa));
 /*B*/   
 /*L*/   sa.sa_handler = handler;
 /*O*/   
 /*C*/   if(sigaction(SIGINT, &sa, NULL) == -1)
 /*K*/         {perror("sigaction"); exit(errno);}
 /**/   
 /**/    /* unmask all signals */
 /**/    if( sigemptyset(&set) == -1 )
 /**/      {perror("sigepmtyset"); exit(errno);}
 /**/   
 /**/    if(sigprocmask(SIG_SETMASK,&set,NULL) == -1 )
 /**/        {perror("sigprocmask"); exit(errno);}



  for(i=0;i<5;++i)
  {
    if((pid = fork()) == -1)
      { perror("rec:fork"); exit(errno); }

    if(pid == 0)/* figlio */
    {

      /* SAME HANDLER BLOCK IS HERE */        

      foo(i);

      return;

    }
    sleep(1);
  }

  return 0;
 }

функция фу

 void foo(int i)
{
  int k;

  for(k=0; k<10; ++k)
  {
    printf("%d. fork %d. print\n", i, k);
    sleep(1);
  }
}

обработчик сигнала

  void handler (int signum) {

  struct timespec sig1;
  struct timespec sig2;

  if(double_sigint == 0)
  {
    if(clock_gettime(CLOCK_REALTIME, &sig1))
      { perror("failed to get sig1 time"); exit(errno); }

    write(1,"Received single SIGINT\n",18);

    double_sigint = 1;

  }
  else if(double_sigint == 1)
  {
     if(clock_gettime(CLOCK_REALTIME, &sig2))
       { perror("failed to get sig2 time"); exit(errno); }

     time_diff = (sig2.tv_sec - sig1.tv_sec) + (sig2.tv_nsec - sig1.tv_nsec)/1000000000;

     if(time_diff < 1)
     {
       double_sigint = 2;  
       write(1,"Received double SIGINT\n",18);
       _exit(EXIT_FAILURE);

     }
     else
     {
      sig1.tv_sec = sig2.tv_sec;
      sig1.tv_nsec = sig2.tv_nsec;

      write(1,"Received single SIGINT\n",18);

     }

  }

}

2 ответа

Когда вы получаете двойной SIGINT, вы убиваете только родительский процесс со строкой _exit(EXIT_FAILURE);, Форки, которые вы создали ранее, не уничтожаются и продолжают работать, их родительский процесс теперь является процессом init.

Если вы хотите, чтобы все дети прекратили, вы должны убить их вручную. Может быть, этот пост будет полезен: как заставить дочерний процесс умереть после выхода из родительского процесса

Изменить: это не было проблемой, так как Ctrl+C отправляет SIGINT всем детям (см. Комментарии). Что сработало для меня:

  • Как сказано в комментарии Уильяма Перселла, создайте глобальные переменные sig1 и sig2.
  • Сделать родительский процесс всегда запущенным (только что добавил while (1); до оператора возврата), поскольку некоторые сигналы не были приняты во внимание после завершения родительского процесса.

В обработчике в предложении else (double_sigint == 1) вы сравниваете sig2 и sig1, но sig1 не инициализирован. Значение, которое вы указали при первом вызове обработчика, исчезло при возврате этого обработчика. Вы можете просто указать область видимости этих переменных.

Используя неинициализированное значение локальной переменной, вы получаете неопределенное поведение. Если обработчик сигнала вызывается и стек обработки сигнала оказывается в том же состоянии, в котором он находился при предыдущем вызове, то все может работать нормально. Это может произойти, если вы отправите сигнал дважды, например, без промежуточных сигналов. Поскольку сон, скорее всего, реализован с помощью сигнала, вполне вероятно, что стек был изменен с момента предыдущего вызова, и sig1 не соответствует вашим ожиданиям. Однако рассуждать о неопределенном поведении несколько бессмысленно.

Другие вопросы по тегам