Monday, October 13, 2014

Unix Prog: sigsetjmp, siglongjmp

1. System Definition:

 ubuntu@ip-172-31-23-227:~$ less /usr/include/setjmp.h  
 ......  
 /* Store the calling environment in ENV, also saving the  
   signal mask if SAVEMASK is nonzero. Return 0.  
   This is the internal name for `sigsetjmp'. */  
 extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROWNL;  
 ......  
 /* Store the calling environment in ENV, also saving the  
   signal mask if SAVEMASK is nonzero. Return 0. */  
 # define sigsetjmp(env, savemask)    __sigsetjmp (env, savemask)  
 ......  
 /* Jump to the environment saved in ENV, making the  
   sigsetjmp call there return VAL, or 1 if VAL is 0.  
   Restore the signal mask if that sigsetjmp call saved it.  
   This is just an alias `longjmp'. */  
 extern void siglongjmp (sigjmp_buf __env, int __val)  
   

When program enter the signal handler(or it catches the signal), the signal is masked off. And when program leave the signal handler, the signal is turned on again.

If using setjmp, longjmp in signal handler to leave the function, the masked signal maybe masked off for ever. sigsetjmp can use the 2nd argument: savemask to indicate whether we should restore the signal mask when jumping out of signal handler(non-zero means yes, 0 means no), and when siglongjmp come back, the saved signal mask is restored.

2. Example:
signal.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<setjmp.h>  
 #include<time.h>  
 #include<signal.h>  
 #include<errno.h>  
   
 sigjmp_buf jmpbuf;  
 volatile sig_atomic_t canjump;  
   
 void pr_mask(const char* str)  
 {  
  sigset_t sigset;  
  int errno_save;  
   
  // Backup the errno value since this function may change it  
  errno_save = errno;  
   
  // Get the current signal mask  
  if(sigprocmask(0, NULL, &sigset) < 0) {  
   printf("sigprocmask error!\n");  
   exit(1);  
  }  
   
  printf("%s", str);  
   
  // Output the signal mask  
  if(sigismember(&sigset, SIGINT)) printf("SIGINT ");  
  if(sigismember(&sigset, SIGQUIT)) printf("SIGQUIT ");  
  if(sigismember(&sigset, SIGUSR1)) printf("SIGUSR1 ");  
  if(sigismember(&sigset, SIGALRM)) printf("SIGALRM ");  
   
  printf("\n");  
   
  //Restore the errno value  
  errno = errno_save;  
 }  
   
 void sig_usr1(int signo)  
 {  
  time_t starttime;  
   
  // Precheck the canjump variable  
  if(canjump == 0)  
   return;  
   
  pr_mask("starting sig_usr1: ");  
   
  // Setup alarm for 3 seconds and let program wait for 5 seconds  
  // During this period, sig_alrm will start running, and then return back  
  alarm(3);  
  starttime = time(NULL);  
  for(;;)  
   if(time(NULL) > starttime + 5) break;  
  pr_mask("finishing sig_usr1: ");  
   
  // Turn off the canjump, to skip future SIGUSR1 signal  
  canjump = 0;  
  siglongjmp(jmpbuf, 1);  
 }  
   
 void sig_alrm(int signo)  
 {  
  pr_mask("in sig_alrm: ");  
 }  
   
 int main(int argc, char* argv[])  
 {  
  // Setup the signal handler for SIGUSR1, SIGALRM  
  if(signal(SIGUSR1, sig_usr1) == SIG_ERR) {  
   printf("signal error!\n");  
   exit(1);  
  }  
   
  if(signal(SIGALRM, sig_alrm) == SIG_ERR) {  
   printf("signal error!\n");  
   exit(2);  
  }  
   
  pr_mask("starting main: ");  
   
  // Setup the signal jump entry  
  if(sigsetjmp(jmpbuf, 1)) {  
   pr_mask("ending main: ");  
   exit(0);  
  }  
   
  // Note: the reason to setump canjump after "sigsetjmp" here is to prevent  
  // the SIGUSR1 signal come before sigssetjmp. Please keep in mind that,  
  // signal can come at any time to interrupt the normal codeflow. And the  
  // program need to prepare for that.  
  canjump = 1;  
   
  // Pause function to wait for signal coming  
  for(;;)  
   pause();  
   
  exit(0);  
 }  

shell:
We start the program in the background, and pr_mask output current signal mask when just "starting main": empty. Then we input "kill -USR1" to send signal to this process, the program then enter sig_usr1, at this time, SIGUSR1 is masked off. After 3 seconds, the sig_alrm catch the SIGALRM signal, inside sig_alrm, current program masked off SIGUSR1, SIGALRM. When the program leaves sig_alrm, the current masked signal is only SIGUSR1. In the end, siglongjmp jump out of signal handler, and current signal mask is restored.
 ubuntu@ip-172-31-23-227:~$ ./signal.out &  
 [1] 21413  
 ubuntu@ip-172-31-23-227:~$ starting main:  
   
 ubuntu@ip-172-31-23-227:~$ kill -USR1 21413  
 ubuntu@ip-172-31-23-227:~$ starting sig_usr1: SIGUSR1  
 in sig_alrm: SIGUSR1 SIGALRM  
 finishing sig_usr1: SIGUSR1  
 ending main:  
   
 [1]+ Done          ./signal.out  

No comments:

Post a Comment