信号处理函数可以正常返回,也可以调用其他函数返回到程序的主函数中,而不是从该处理程序返回。
ANSI C标准有说明:一个信号处理程序可以返回或者调用 abort、exit 或 longjmp。( goto 不支持跳出它所在的函数,因此不能用来从信号处理程序返回到主函数中 )。
setjmp() 和 longjmp() 的原型如下:
int setjmp( jmp_buf env );
void longjmp( jmp_buf env, int val );
说明:
longjmp() 可以跳转到 setjmp() 设置的位置。
参数 env 是一个特殊类型 jmp_buf 变量。这一数据类型是某种形式的数组,里面存放的是在调用 longjmp() 时能用来恢复栈状态的所有信息。一般说来,env 是个全局变量,因为需要从另一个函数中引用它。我们可以在希望返回的位置使用 setjmp()。直接调用 setjmp() 时返回 0 ;当从 longjmp() 返回时,setjmp() 的返回值是 longjmp() 的第 2 个参数的值,利用这一点使多个 longjmp() 返回到一个 setjmp() 处。
测试程序:
[C++] 纯文本查看 复制代码 #include <stdio.h>
#include <setjmp.h>
#include <signal.h>
jmp_buf env; //保存待跳转位置的栈信息
/*信号 SIGRTMIN+15 的处理函数*/
void handler_sigrtmin15(int signo)
{
printf("recv SIGRTMIN+15\n");
longjmp(env, 1);
}
/*信号 SIGRTMAX-15 的处理函数*/
void handler_sigrtmax1(int signo)
{
printf("recv SIGRTMAX-1\n");
longjmp(env, 2);
}
int main()
{
/*设置返回点*/
switch(setjmp(env)) {
case 0:
break;
case 1:
printf("return from SIGRTMIN+15\n");
break;
case 2:
printf("return from SIGRTMAX-1\n");
break;
default:
break;
}
/*捕捉信号,安装信号处理函数*/
signal(SIGRTMIN+15, handler_sigrtmin15);
signal(SIGRTMAX-1, handler_sigrtmax1);
while(1)
;
return 0;
}
说明:
在程序中设置了两个实时信号 SIGRTMIN+15 和 SIGRTMAX-1 的信号处理函数 handler_sigrtmin15() 与 handler_sigrtmax1() 。
当运行程序时,在另外的一个 shell 里输入以下命令:
beyes@linux-beyes:~> kill -s SIGRTMIN+15 16345 #16345 是通过 ps -a 命令得到得上面程序的 PID beyes@linux-beyes:~> kill -s SIGRTMIN+15 16345
这时,可以在程序运行窗口看到:beyes@linux-beyes:~/C> ./wrong_return.exe
recv SIGRTMAX-1
return from SIGRTMAX-1
recv SIGRTMIN+15
return from SIGRTMIN+15 但是,如果在第二个终端使用 kill 再发送信号时,程序运行终端里再也看不出输出,也就是信号不处理了!原因是:
默认情况下(不指定 sa_flags 为 SA_NOMASK),信号处理时会自动阻塞正在被处理的信号。也就是说,和正在被处理同样的信号再次发生时,它会被阻塞到本次信号处理结束。在信号处理函数返回时,进程的信号屏蔽字会被恢复,即解除对当前信号的阻塞。在上面的程序中,并没有让信号处理器正常返回,而是使用了 longjmp() 直接跳转,所以进程的信号屏蔽字在第一次收到信号后,就把信号设置为阻塞并且再也没有恢复,因而再也触发不了信号处理函数了,除非手动将进程对信号的屏蔽去处。
如果既想使用跨函数跳转直接返回,又想避免每次手动清除信号屏蔽的麻烦,就要使用 sigsetjmp() 和 siglongjmp() 函数。
下图描述了 setjmp() 和 longjmp() 的使用情形:
![]()
|