10.15 sigstjmp以及siglongjmp函数
在7.10节中,我们讲述了setjmp以及longjmp函数,这两个函数用于远程跳转。其中longjmp函数进场在信号处理函数中被调用,用于返回到进程的主循环中去,而不是从信号处理函数中返回。
但是使用函数longjmp有一个问题,当一个信号被捕获的时候,信号处理函数被执行,当前信号将被自动增加到进程的信号掩码中去。这样做的目的是为了防止后续的信号中断当前信号处理函数的执行,如果我们调用longjmp函数退出信号处理函数,信号掩码将会发生什么呢?
在FreeBSD 8.0以及Mac OS X 10.6.8中,setjmp以及longjmp函数保存和恢复信号掩码,Linux 3.2.0以及Solaris 10并没有这么做,虽然Linux支持通过选项来提供BSD的行为。FreeBSD以及Mac OS X提供了函数_setjmp以及_longjmp函数,这两个函数并不保存和恢复信号掩码。
为了允许两种行为方式,POSIX.1并没有指定setjmp和longjmp函数的对于信号掩码的影响,取而代之的是,增加了两个新函数sigsetjmp以及siglongjmp函数,当从信号处理函数中进行跳转的时候一定要使用这两个函数。
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
Returns:0 if called directly,nonzero if returning from a call to siglongjmp
void siglongjmp(sigjmp_buf env, int val);
这两个函数与setjmp和longjmp函数的差异就是sigsetjmp有一个额外的参数。当参数savemask是非零数值的时候,sigsetjmp会将当前信号掩码一起去保存到env中,当siglongjmp函数被调用的时候,如果sigsetjmp调用的时候使用的是非零的savemask来保存的env.那么siglongjmp函数就会恢复已经保存的信号掩码。
example
#include "apue.h"
#include <setjmp.h>
#include <time.h>
static void sig_usr1(int);
static void sig_alarm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
int main(void)
{
if(signal(SIGUSR1, sig_usr1) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if(signal(SIGALRM, sig_alarm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
pr_mask("starting main: "); /* Figure 10.14 */
if(sigsetjmp(jmpbuf, 1))
{
pr_mask("ending main: ");
exit(0);
}
canjump = 1; /*now sigsetjmp() is OK*/
for(;;)
pause();
}
static void sig_usr1(int signo)
{
time_t starttime;
if(canjump == 0)
return; /*unspected signal, ignore */
pr_mask("starting sig_usr1: ");
alarm(3); /*SIGALRM in 3 seconds */
starttime = time(NULL);
for(;;)
{
if(time(NULL) > starttime+5)
break;
}
pr_mask("finishing sig_usr1: ");
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to main, don't return */
}
static void sig_alarm(int signo)
{
pr_mask("in sig_alarm: ");
}
执行效果如下所示:
os@debian:~/UnixProgram/Chapter10$ ./10_20.exe &
[3] 2566
os@debian:~/UnixProgram/Chapter10$ starting main: over
kill -USR1 2566
starting sig_usr1: SIHUSR1 over
os@debian:~/UnixProgram/Chapter10$ in sig_alarm: SIHUSR1 SIGALRM over
finishing sig_usr1: SIHUSR1 over
ending main: over
[3]+ Done ./10_20.exe
os@debian:~/UnixProgram/Chapter10$