sigsetjmp 和 siglongjmp 函数

在[url=http://aisxyz.iteye.com/admin/blogs/2391169]非局部跳转函数 setjmp 和 longjmp 介绍[/url]一节中曾提到用于在普通函数中进行非局部转移的 setjmp 和 longjmp 函数,POSIX.1 没有指定指定这两个函数对信号屏蔽字的作用,而是定义了两个新函数 sigsetjmp 和 siglongjmp。在信号处理程序中进行非局部转移时应当使用这两个函数。

#include <setjmp.h>
int sigsetjmp(sigjmp_buf, int savemask);
/* 返回值:若直接调用,返回 0;若从 siglongjmp 调用返回,则返回非 0 */
void siglongjmp(sigjmp_buf env, int val);

这两个函数与 setjmp、longjmp 之间的唯一区别是 sigsetjmp 增加了一个参数:如果 savemask 非 0,则 sigsetjmp 在 env 中保存进程的当前信号屏蔽字,以便调用 siglongjmp 时从其中恢复保存的信号屏蔽字。
下面这个程序演示了在信号处理程序被调用时,系统所设置的信号屏蔽字如何自动地包括被捕捉到的信号。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <setjmp.h>
#include <errno.h>

// extern void pr_mask(const char *);
void pr_mask(const char *str){
sigset_t sigset;
int errno_save;

errno_save = errno; // we can be called by signal handlers
if(sigprocmask(0, NULL, &sigset) < 0){
printf("sigprocmask error\n");
}else{
printf("%s", str);
if(sigismember(&sigset, SIGINT))
printf(" SIGINT");
if(sigismember(&sigset, SIGQUIT))
printf(" SIGQUIT");
if(sigismember(&sigset, SIGUSR1))
printf(" SIGUSR1");
if(sigismember(&sigset, SIGALRM))
printf(" SIGALRM");

/* remaining signals can go here */

printf("\n");
}
errno = errno_save; // restroe errno
}

static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;

static void sig_usr1(int signo){
if(canjump == 0)
return; // unexpected signal, ignore
pr_mask("starting sig_usr1:");

alarm(3); // SIGALRM in 3 seconds
time_t starttime = time(NULL);
for(;;) // busy wait for 5 seconds
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_alrm(int signo){
pr_mask("in sig_alrm:");
}

int main(void){
if(signal(SIGUSR1, sig_usr1) == SIG_ERR)
printf("signal(SIGUSR1) error\n");
if(signal(SIGALRM, sig_alrm) == SIG_ERR)
printf("signal(SIGALRM) error\n");
pr_mask("starting main:");
if(sigsetjmp(jmpbuf, 1)){
pr_mask("ending main:");
exit(0);
}
canjump = 1; // now sigsetjmp() is OK
for(;;)
pause();
}

此程序中演示了另一种只要在信号处理程序中调用 siglongjmp 就应使用的技术:仅在调用 sigsetjmp 之后才将变量 canjump 设置为非 0,在信号处理程序中检测此变量,仅当它为非 0 时才调用 siglongjmp。这提供了一种保护机制,以免 jmpbuf 还未初始化时就调用信号处理程序,因为信号可能在任何时候发生。
另外,程序中使用了数据类型 sig_atomic_t,在写这种类型的变量时不会被中断。这意味着这种变量在具有虚拟存储器的系统上不会跨越页边界,可以用一条机器指令对其进行访问。这种类型的变量总是包括类型修饰符 volatile,其原因是:该变量将由两个不同的控制线程 main 函数和异步执行的信号处理程序访问。
下图显示了此程序的执行时间顺序。在进程执行左面部分时(对应 main),信号屏蔽字是 0(无信号被阻塞);执行中间部分是(对应 sig_usr1),其信号屏蔽字是 SIGUSR1;执行右面部分时(对应 sig_alrm),其信号屏蔽字是 SIGUSR1 和 SIGALRM。
[img]http://dl2.iteye.com/upload/attachment/0127/2513/75427673-faa3-3ca2-b1ae-f7c05af000af.png[/img]
运行该程序的结果如下。

$ ./sigjump.out & # 在后台启动程序
[1] 16011
$ starting main: # 这是 main 函数的输出
$
$ kill -USR1 16011 # 向该后台进程发送 SIGUSR1 信号
starting sig_usr1: SIGUSR1
in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main:
[1]+ Done ./sigjump.out

可见该输出与所期望的相同:当调用一个信号处理程序时,被捕捉到的信号会自动加到进程的当前信号屏蔽字中。当从信号处理程序返回时,siglongjmp 恢复了 sigsetjmp 所保存的信号屏蔽字。
如果在 Linux 中将该程序中的 sigsetjmp 和 siglongjmp 分别替换成 setjmp 和 longjmp,则最后一行输出将变成:
ending main: SIGUSR1
这意味着在调用 setjmp 之后执行 main 函数时,其 SIGUSR1 是阻塞的,这多半不是所期望的。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值