1.在信号处理程序中进行非局部跳转时,应该的使用的两个函数(信号处理程序中不要使用setjmp 和 longjmp函数)。(有关这四个函数:setjmp,longjmp 和 sigsetjmp,siglongjmp 的参数含义、它们如何返回和如何跳转,以及其他信号相关函数,请参看其他资料)
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask); 返回值:若直接调用返回0,若从siglongjmp调用返回则返回非0值 void siglongjmp(sigjmp_buf env, int val);
|
说明:这两个函数需要配合使用。如果 savemask 非0, 则sigsetjmp 在 env 中保存进程的当前信号屏蔽字。调用siglongjmp时,如果前面步骤中的sigsetjmp 函数的savemask非0,则siglongjmp 函数恢复保存的信号屏蔽字。
2.实例代码
下面的程序演示了在信号处理程序被调用时,系统所设置的信号屏蔽字如何自动地包括刚被捕捉到的信号。该程序也通过实例说明了如何使用 sigsetjmp 和 siglongjmp 函数。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <time.h>
static void sig_usr1(int), sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
#include <errno.h>
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)
perror("sigprocmask error");
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;
}
int main(void)
{
if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
perror("signal(SIGUSR1) error");
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
perror("signal(SIGALRM) error");
pr_mask("starting main: ");
if (sigsetjmp(jmpbuf, 1)) {
pr_mask("ending main: ");
exit(0);
}
canjump = 1; /* now sigsetjmp() is OK */
for ( ; ; )
pause();
}
time_t starttime;
static void sig_usr1(int signo)
{
if (canjump == 0)
return; /* unexpected signal, ignore */
pr_mask("starting sig_usr1: ");
alarm(3); /* SIGALRM in 3 seconds */
starttime = time(NULL);
for ( ; ; ) /* busy wait for 5 seconds */
if (time(NULL) > starttime + 5)
break;
printf("in sig_usr1, interval = %d\n", time(NULL) - starttime);
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: ");
printf("in sig_alrm, interval = %d\n", time(NULL) - starttime);
}
|
编译后执行结果:
[liumin@localhost sigsetjmp]$ ./m &
[1] 18018
[liumin@localhost sigsetjmp]$ starting main:
[liumin@localhost sigsetjmp]$ kill -SIGUSR1 18018
[liumin@localhost sigsetjmp]$ starting sig_usr1: SIGUSR1
in sig_alrm: SIGUSR1 SIGALRM
in sig_alrm, interval = 3
in sig_usr1, interval = 6
finishing sig_usr1: SIGUSR1
ending main:
[1]+ Done ./m
[liumin@localhost sigsetjmp]$
|
此程序演示了另一种技术,只要在信号处理程序中调用siglongjmp,就应使用这种技术。仅在调用 sigsetjmp 之后才将变量 canjump 设置为非0值。在信号处理程序中检测此变量,仅当它为非0值时才调用siglongjmp。这提供了一种保护机制,使得在jmpbuf尚未由sigsetjmp 初始化时,防止调用信号处理程序。另外,在本程序中,调用siglongjmp之后程序很快就结束,但是在较大的程序中,在调用siglongjmp 之后的一段较长时间内,信号处理程序可能仍旧被设置,通过检测canjump,就可以起到保护作用。
在一般的 C 代码中(不是信号处理程序),对于longjmp并不需要这种保护措施。但是,因为信号可能在任何时候发生,所以在信号处理程序中,需要这种保护措施。
在程序中使用了数据类型 sig_atomic_t,这是由 ISO C 标准定义的变量类型,在写这种类型的变量时不会被中断。它意味着在具有虚拟存储器的系统上这种变量不会跨越页边界,可以用一条机器指令对其进行访问。这种类型的变量总是包括 ISO 类型修饰符 volatile,其原因是:该变量将由两个不同的控制线程--main 函数和异步执行的信号处理程序访问。
阅读(2455) | 评论(1) | 转发(0) |