0

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. 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. 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.

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? Should i call killl or some other function in handler to terminate them?

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

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

     }

  }

}
user3717434
  • 215
  • 4
  • 19
  • I suppose this is a toy/demo program. What's the real problem you're trying to solve? – John Zwinck Jul 19 '14 at 12:39
  • real problem is a more complicating program that a main program launches servers as forks and in the same way described above signal handler print some output at single SIGINT and print again and terminate at double SIGINT. But i need to understand signal/fork problem anyway – user3717434 Jul 19 '14 at 12:46
  • in addition, each server has socket connection and a worker thread to handle it, so when arrives a single SIGINT functions like read shouldn't be interrupted. But in this case they don't get interrupted also with double SIGINT. What should i do? – user3717434 Jul 19 '14 at 12:53
  • Style suggestion: rather than putting "handler block" in a column comment to the left of the code, call a function with a name like "initialize_signal_handlers" – William Pursell Jul 19 '14 at 14:45
  • It would be really helpful if people could cut and paste your code. By splitting it into 3 sections and failing to include the header files (the #includes replaced by the comment /*libraries*/), you make life difficult for anyone trying to help you. – William Pursell Jul 19 '14 at 14:54
  • @WilliamPursell thanks for the suggestions, I was thinking it's easier to read splitted code and just copy past would not be difficult. – user3717434 Jul 19 '14 at 15:08
  • Now that I've cleaned up your code enough to run it, it works exactly as you seem to expect it to. Perhaps I don't understand your expectation. – William Pursell Jul 19 '14 at 15:31
  • Note that there is no reason to re-establish the signal dispositions in the child: they are inherited across the fork. – William Pursell Jul 19 '14 at 16:25

2 Answers2

0

When you receive a double-SIGINT, you only kill the parent process, with the line _exit(EXIT_FAILURE);. The forks you have created before are not killed and keep running, their parent now being the init process.

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). What worked for me was :

  • As said in William Pursell's comment, make sig1 and sig2 global variables.
  • 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.
Community
  • 1
  • 1
BlackDwarf
  • 2,070
  • 1
  • 17
  • 22
  • Thanks a lot! But it still have some problems. I used `prctl(PR_SET_PDEATHSIG, SIGHUP` solution and put it in fork block just before calling `foo(i)`. I was thinking that i "should" block the SIGINT in the fork block, but if i do it, handler works properly(single SIGINT prints, double SIGINT terminates) but somehow forks print only five or less messages even if I don't send any signal(they should print 10 messages) And if i don't block the SIGINT, when i send SIGINT, it prints "Received single SIGINT" many times and still just prints five or less messages for each fork. What i'm missing? – user3717434 Jul 19 '14 at 14:34
  • No, the children should also receive the SIGINT since they are still in the controlling process of the tty that spawned them. – William Pursell Jul 19 '14 at 15:35
  • True, it works with Ctrl+C in the terminal, but it won't work if the signal is sent with `kill` or `signal()`, and in this example it won't work once the parent process is terminated. – BlackDwarf Jul 19 '14 at 16:08
0

In the handler, in the else clause (double_sigint == 1) you are comparing sig2 and sig1, but sig1 is uninitialized. 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. However, speculation about undefined behavior is somewhat pointless.

William Pursell
  • 204,365
  • 48
  • 270
  • 300