vlambda博客
学习文章列表

Unix 环境编程10-15 sigjmp longjmp 和 sigsetjmp siglongjmp的比较

章目录

    • 实验一:信号函数handler 中再次触发同一个信号

    • 实验二 使用longjmp 跳回到main函数中

    • 实验三 使用siglongjmp 跳回到main函数中


UNIX 环境高级编程10-15 介绍siglongjmp和sigsetlongjmp 函数,这里通过3个例子加深理解。


实验一:信号函数handler 中再次触发同一个信号

首先看一般情况下,如果在一个信号执行函数中再此触发同样的信号会是则怎样的情况

#include <stdlib.h> #include <unistd.h>#include <stdio.h>#include <signal.h>#include <setjmp.h> void handler(int a){ sigset_t sigpend; sigset_t sigmask; sleep(5); sigpending(&sigpend); sigprocmask(0,NULL,&sigmask); if(sigismember(&sigpend,SIGQUIT)) { printf("SIGQUIT is pending\n"); } else { printf("SIGQUIT is not pending\n"); } if(sigismember(&sigmask,SIGQUIT)) { printf("SIGQUIT is in sigmask\n"); } else { printf("SIGQUIT is not in sigmask\n"); } } int main() { int sleep_left; sigset_t sigmask; if(signal(SIGQUIT,handler)==SIG_ERR) { printf("signal error\n"); exit(1); } sleep_left = sleep(5);   sigprocmask(0,NULL,&sigmask); if(sigismember(&sigmask,SIGQUIT)) { printf("main : SIGQUIT is in sigmask\n"); } else { printf("main : SIGQUIT is not in sigmask\n"); }  printf("sleep_left = %d\n",sleep_left); exit(0); }

运行结果如下

$ ./test ^\^\SIGQUIT is pendingSIGQUIT is in sigmaskSIGQUIT is not pendingSIGQUIT is in sigmaskmain : SIGQUIT is not in sigmasksleep_left = 3

结果分析:

首先在main中遇到sleep时,触发第一次SIGQUIT信号,当遇到handler中的sleep函数时触发第二次SIGQUIT信号。

第一次进入handler函数时,SIGQUIT就被加入了屏蔽字中,因此SIGQUIT 会在sigmask中,而此时发出来第二次的SIGQUIT信号,因此SIGQUIT信号会被阻塞则会处于pending状态。

第一次handler执行完之后,SIGQUIT信号会从屏蔽字中去除,因此则会处理第二次的SIGQUIT信号,第二次进入handler函数中,此时SIGQUIT依旧会被加入到屏蔽字中,但是由于没有第三个SIGQUIT信号,SIGQUIT不会处于pending状态。

而回到main函数中时,屏蔽字则会恢复,因此SIGQUIT不会在屏蔽字中。


实验二 使用longjmp 跳回到main函数中

#include <stdlib.h> #include <unistd.h>#include <stdio.h>#include <signal.h>#include <setjmp.h> static jmp_buf jmpbuf; void handler(int a){ sigset_t sigpend; sigset_t sigmask; sleep(5); sigpending(&sigpend); sigprocmask(0,NULL,&sigmask); if(sigismember(&sigpend,SIGQUIT)) { printf("SIGQUIT is pending\n"); } else { printf("SIGQUIT is not pending\n"); } if(sigismember(&sigmask,SIGQUIT)) { printf("SIGQUIT is in sigmask\n"); } else { printf("SIGQUIT is not in sigmask\n"); } longjmp(jmpbuf,1);}int main(){  sigset_t sigmask; sigset_t sigpend; if(signal(SIGQUIT,handler)==SIG_ERR) { printf("signal error\n"); exit(1); } while(setjmp(jmpbuf)==0);  sigprocmask(0,NULL,&sigmask); if(sigismember(&sigmask,SIGQUIT)) { printf("main : SIGQUIT is in sigmask\n"); } else { printf("main : SIGQUIT is not in sigmask\n"); }  sigpending(&sigpend); if(sigismember(&sigpend,SIGQUIT)) { printf("main : SIGQUIT is pending\n"); } else { printf("main : SIGQUIT is not pending\n"); }   exit(0); }


实验结果如下:


$ ./test^\^\SIGQUIT is pendingSIGQUIT is in sigmaskmain : SIGQUIT is in sigmaskmain : SIGQUIT is pending




结果分析:


首先在main中遇到sleep时,触发第一次SIGQUIT信号,当遇到handler中的sleep函数时触发第二次SIGQUIT信号。

第一次进入handler函数时,SIGQUIT就被加入了屏蔽字中,因此SIGQUIT 会在sigmask中,而此时发出来第二次的SIGQUIT信号

因此SIGQUIT信号会被阻塞则会处于pending状态。

第一次handler执行完之后,则会跳转到setjmp函数的位置,此时SIGQUIT仍然屏蔽字中,因此第二次的SIGQUIT信号就不会进入handler函数中,因此在main函数中会输出SIGQUIT在屏蔽字中,SIGQUIT处在pending状态。

而回到main函数中时,屏蔽字则会恢复,因此SIGQUIT不会再屏蔽字中。


实验三 使用siglongjmp 跳回到main函数中

#include <stdlib.h> #include <unistd.h>#include <stdio.h>#include <signal.h>#include <setjmp.h> static jmp_buf jmpbuf; void handler(int a){ sigset_t sigpend; sigset_t sigmask; sleep(5); sigpending(&sigpend); sigprocmask(0,NULL,&sigmask); if(sigismember(&sigpend,SIGQUIT)) { printf("SIGQUIT is pending\n"); } else { printf("SIGQUIT is not pending\n"); } if(sigismember(&sigmask,SIGQUIT)) { printf("SIGQUIT is in sigmask\n"); } else { printf("SIGQUIT is not in sigmask\n"); } siglongjmp(jmpbuf,1);}int main(){  sigset_t sigmask; sigset_t sigpend; if(signal(SIGQUIT,handler)==SIG_ERR) { printf("signal error\n"); exit(1); } while(sigsetjmp(jmpbuf,1)==0);  sigprocmask(0,NULL,&sigmask); if(sigismember(&sigmask,SIGQUIT)) { printf("main : SIGQUIT is in sigmask\n"); } else { printf("main : SIGQUIT is not in sigmask\n"); }  sigpending(&sigpend); if(sigismember(&sigpend,SIGQUIT)) { printf("main : SIGQUIT is pending\n"); } else { printf("main : SIGQUIT is not pending\n"); }   exit(0); }

实验结果如下:


$ ./test ^\^\SIGQUIT is pendingSIGQUIT is in sigmaskSIGQUIT is not pendingSIGQUIT is in sigmaskmain : SIGQUIT is not in sigmaskmain : SIGQUIT is not pending





结果分析:


首先在main中遇到sleep时,触发第一次SIGQUIT信号,当遇到handler中的sleep函数时触发第二次SIGQUIT信号。

第一次进入handler函数时,SIGQUIT就被加入了屏蔽字中,因此SIGQUIT 会在sigmask中,而此时发出来第二次的SIGQUIT信号

因此SIGQUIT信号会被阻塞则会处于pending状态。

第一次handler执行完之后,则会跳转到sigsetjmp函数的位置,此时屏蔽字已经被恢复中,因此第二次的SIGQUIT信号就会进入handler函数中,此时由于没有第三个SIGQUIT信号,所以输出SIGQUIT在屏蔽字中,但不在pending中。随后再次使用siglongjmp跳入main函数中。

回到main函数中,屏蔽字被恢复,因此在main函数中会输出SIGQUIT不在在屏蔽字中,SIGQUIT不处在pending状态。


这里需要注意的时sigsetjmp函数可以设置跳转时是否恢复屏蔽字,上面的例子中设置sigsetjmp(jmpbuf,1)代表会恢复。

而设置sigsetjmp(jmpbuf,0)则不会恢复。


$ ./test^\^\SIGQUIT is pendingSIGQUIT is in sigmaskmain : SIGQUIT is in sigmaskmain : SIGQUIT is pending




最后通过这三个例子做一个总结:

1、信号处理程序正常结束,会自动加触发信号加入屏蔽字,信号处理函数结束,会恢复原来的信号屏蔽字。

2、longjmp跳出信号处理程序,不会恢复原来的信号屏蔽字。

3、siglongjmp的第二个参数非零,恢复信号屏蔽字,为0则不恢复。