简体   繁体   English

到达双精度SIGINT时终止主程序和所有fork的信号处理程序

[英]signal handler to terminate the main program and all forks when arrives a double SIGINT

I really didn't get how signal handlers work especially with forks. 我真的不知道信号处理程序如何工作,尤其是在使用分叉时。 So i need to do this exercise but i couldn't get it work properly. 所以我需要做这个练习,但是我无法正常工作。

My main program makes 5 forks, each fork prints simply 10 messages with its pid. 我的主程序制作了5个fork,每个fork的pid仅打印10条消息。 So the purpose of the program, when i send a SIGINT signal via keyboard(Ctrl-c) it should print, "a single SIGINT arrived", if two SIGINT arrives between one second, it should print "double SIGINT arrived" and should terminate the whole program. 因此,该程序的目的是,当我通过键盘(Ctrl-c)发送SIGINT信号时,它应该打印“单个SIGINT到达”,如果两个SIGINT在1秒钟之间到达,则应打印“ double SIGINT到达”并终止整个程序。 So when i launch my program, it handles first two SIGINT(that i send the second more than 1 second after the first one) but then it doesn't handle single SIGINT and neither double SIGINT. 因此,当我启动程序时,它将处理前两个SIGINT(我将第一个发送的第二个SIGINT发送超过第二个SIGINT),但随后它不处理单个SIGINT和两个SIGINT。

So i'm very confused about signals. 所以我对信号非常困惑。 Forks continue to stamp messages. 叉继续标记邮件。 I load same handler both to main and to forks but what should i do to terminate all forks when arrives double SIGINT? 我将相同的处理程序加载到main和fork中,但是当到达double SIGINT时我应该怎么做才能终止所有fork? Should i call killl or some other function in handler to terminate them? 我应该在处理程序中调用killl或其他函数来终止它们吗?

the main function 主要功能

 /* 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 function foo函数

 void foo(int i)
{
  int k;

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

signal handler 信号处理器

  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);

     }

  }

}

When you receive a double-SIGINT, you only kill the parent process, with the line _exit(EXIT_FAILURE); 当您收到一个双SIGINT时,您只用行_exit(EXIT_FAILURE);杀死父进程_exit(EXIT_FAILURE); . The forks you have created before are not killed and keep running, their parent now being the init process. 您之前创建的fork不会被杀死并继续运行,它们的父级现在是init进程。

If you want all the children to terminate, you have to kill them manually. 如果要终止所有孩子,则必须手动杀死他们。 Maybe this post would be helpful : How to make child process die after parent exits 也许这篇文章会有所帮助: 如何使子进程在父母退出后死亡

Edit: That was not the problem since Ctrl+C sends a SIGINT to all the children (see comments). 编辑:这不是问题,因为Ctrl + C将SIGINT发送给所有子级(请参阅注释)。 What worked for me was : 对我有用的是:

  • As said in William Pursell's comment, make sig1 and sig2 global variables. 如William Pursell的评论所述,使sig1和sig2全局变量。
  • Make the parent process always run (just added a while (1); before the return statement), because some signals were not taken into account once the parent process was terminated. 使父进程始终运行(在return语句之前添加一小段while (1);因为在父进程终止后不会考虑某些信号)。

In the handler, in the else clause (double_sigint == 1) you are comparing sig2 and sig1, but sig1 is uninitialized. 在处理程序中,在else子句(double_sigint == 1)中,您正在比较sig2和sig1,但是sig1未初始化。 The value that you gave it the first time the handler was called went away when that handler returned. 当处理程序返回时,您第一次给处理程序调用的值消失了。 You could simply give those variables file scope. 您可以简单地为这些变量提供文件作用域。

By using the uninitialized value of the local variable, you are getting undefined behavior. 通过使用局部变量的未初始化值,您将获得未定义的行为。 If the signal handler is called and the signal handling stack happens to be in the same state it was on the previous call, then things may work fine. 如果调用了信号处理程序,并且信号处理堆栈恰好与上一次调用处于同一状态,则可能工作正常。 This can happen if you send the signal twice with no intervening signals, for example. 例如,如果您两次发送信号而没有中间信号,则会发生这种情况。 Since sleep is likely implemented with a signal, it is quite likely that the stack has been modified since the previous call and sig1 is not what you expect. 由于睡眠可能是通过信号实现的,因此很可能堆栈已被修改,因为上一次调用和sig1不是您所期望的。 However, speculation about undefined behavior is somewhat pointless. 但是,关于未定义行为的猜测是没有意义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM