信号处理程序在到达双SIGINT时终止主程序和所有分支

时间:2014-07-19 12:37:34

标签: c linux signals fork signal-handling

我真的没有得到信号处理程序如何工作,特别是叉子。所以我需要做这个练习,但我无法正常工作。

我的主程序生成5个分叉,每个分叉只打印10个带有pid的消息。所以程序的目的,当我通过键盘发送SIGINT信号(Ctrl-c)它应该打印,“单个SIGINT到达”,如果两个SIGINT到达一秒之间,它应该打印“双SIGINT到达”并应该终止整个计划。因此,当我启动我的程序时,它会处理前两个SIGINT(我在第一个之后发送第二个超过1秒)但是它不会处理单个SIGINT而且也不会处理双重SIGINT。

所以我对信号非常困惑。福克斯继续加盖邮件。我将相同的处理程序加载到main和toks但是当我到达双SIGINT时我该怎么做才能终止所有的forc?我应该在处理程序中调用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;
 }

foo功能

 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 个答案:

答案 0 :(得分:0)

当您收到双重SIGINT时,只会使用行_exit(EXIT_FAILURE);终止父进程。您之前创建的分叉未被杀死并继续运行,其父级现在是init进程。

如果您希望所有孩子都终止,您必须手动杀死他们。也许这篇文章会有所帮助:How to make child process die after parent exits

编辑:这不是问题,因为Ctrl + C会向所有孩子发送一个SIGINT(请参阅注释)。 对我有用的是:

  • 如William Pursell的评论所述,制作sig1和sig2全局变量。
  • 使父进程始终运行(只在return语句之前添加while (1);),因为一旦父进程终止,一些信号就不会被考虑在内。

答案 1 :(得分:0)

在处理程序中,在else子句(double_sigint == 1)中,您要比较sig2和sig1,但sig1未初始化。当处理程序返回时,第一次调用处理程序时给出的值就消失了。您可以简单地为这些变量提供文件范围。

通过使用局部变量的未初始化值,您将获得未定义的行为。如果调用信号处理程序并且信号处理堆恰好处于上次调用时的状态,那么可能正常工作。例如,如果您发送信号两次而没有中间信号,就会发生这种情况。由于睡眠很可能是通过信号实现的,因此很可能自上一次调用以来sig1已经被修改,并且sig1不是您所期望的。然而,关于未定义行为的猜测有点毫无意义。