曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 8176|回复: 0
打印 上一主题 下一主题

[信号] setjmp() 和 longjmp() -- 跳过栈帧的函数返回

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2009-7-6 00:35:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
信号处理函数可以正常返回,也可以调用其他函数返回到程序的主函数中,而不是从该处理程序返回。
ANSI C标准有说明:一个信号处理程序可以返回或者调用 abort、exit 或 longjmp。( goto 不支持跳出它所在的函数,因此不能用来从信号处理程序返回到主函数中 )。

setjmp() 和 longjmp() 的原型如下
#include <setjmp.h>
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+15SIGRTMAX-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() 的使用情形:


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-7 05:36 , Processed in 0.074516 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表