分类: 系统运维
2012-03-30 19:45:32
在7.10节,我们讨论了setjmp和longjmp函数,它们可以用作非本地分支。longjmp函数通常被一个信号处理器调用来返回到一个程序的主循环,而不是从处理器里返回。
然而调用longjmp有一个问题。当一个信号被捕获时,信号捕获函数被进入,当前信号被自动加到进程的信号掩码。这阻止了信号的后续发生而中断这个信号处理器。如果我们从信号处理器longjmp出去,进程的信号掩码会发生什么呢?
在FreeBSD 5.2.1和Mac OS X 10.3下,setjmp和longjmp存储并恢复信号掩码。然而,Linux 2.4.22和Solaris 9不这样做。FreeBSD和Mac OS X提供了函数_setjmp和_longjmp,它们不存储和恢复信号掩码。
为了允许任一形式的行为,POSIX.1不指定setjmp和longjmp在信号掩码上的效果。相反,POSIX.1定义了两个新的函数sigsetjmp和siglongjmp。在从一个信号处理器分支出去时应该总是使用这两个函数。
这 些函数和setjmp与longjmp函数的唯一区别是sigsetjmp有一个额外的参数。如果savemask是非0值,那么sigsetjmp也在 env里保存进程当前的信号掩码。当siglongjmp被调用时,如果env参数被一个非0的savemask的sigsetjmp调用保存,那么 siglongjmp恢复保存的信号掩码。
下面的代码演示了当一个信号处理器被自动调用时由系统安装的信号掩码如何被捕获的信号。程序也展示了sigsetjmp和siglongjmp函数的使用。
这里,我们使用了数据类型 sig_atomic_t,它由ISO C标准定义为不被中断的写的变量类型。意思是一个这样类型的变量不应该在一个有虚拟内存的系统上跨过页边界,而且可以被单个指定访问。我们总是同时为这些 数据类型包含volatile类型修饰符,因为这个变量被两个不同的信号控制访问:main函数和异步执行的信号处理器。
我们可以把程序分 为三部分。main、sig_usr1和sig_alrm。main函数会先执行,随后是sig_usr1,sig_usr1里的计时器会导致 sig_alrm的执行。在sig_alrm完毕后,返回到sig_usr1,而sig_usr1会返回到main。当执行sig_usr1时,信号掩码 是SIGUSR1。当执行sig_alrm时,信号掩码是SIGUSR1 | SIGALRM。
程序运行结果:
$ ./a.out &
[1] 10787
staring main:
$ kill -USR1 10787
starting sig_usr1: SIGUSR1
$ in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main:
[1]+ 完成 ./a.out
输出如我们所料:当一个信号处理器被调用时,被捕获的信号被加入到进程的当前信号掩码。当信号处理器返回时原始掩码被恢复。同样,siglongjmp恢复由sigsetjmp保存的信号掩码。
如 果我们改变上面的代码在Linux上让setjmp和longjmp调用来代替sigsetjmp和siglongjmp调用(或在FreeBSD上用 _setjmp和_longjmp代替),输出的最终行变为:ending main: SIGUSR1。这意味着main函数在setjmp调用后执行时,SIGUSR1信号被阻塞。这很可能不是我们想要的。