游戏后台开发
分类: LINUX
2013-08-20 15:56:24
sigsetjmp and siglongjmp functions
#include
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);
这两个函数与setlongjmp 和longjmp在使用与signal handler里面的时候的区别在于:
是否可以自动恢复signal mask:
Setlongjmp和longjmp如果在signal handler里面被调用,当longjmp返回时,不会自动将handler里面系统自动block的本signal给unblock。当然,如果我们用return之类的返回语句,就会自动unblock。并且为了兼容setlongjmp和longjmp,sigsetjmp和siglongjmp还可以提供不自动恢复signal mask的方式工作。因此我们基本上在signal handler里面摒弃对longjmp和setlongjmp的调用。
检查位置缓冲的合法性:
如果在signal机制里面使用这种长跳转,建议使用siglongjmp的时候,要确保sigsetjmp已经被调用,即我们可以调到一个合法的位置去。因此要使用一个标记,来表明当前位置缓冲是否合法。当sigsetjmp调用后,标记置位,位置缓冲合法,这样在signal handler里面就可以正确的跳到该位置了。我们之所以要这么做是因为,在signal 处理中,指不定什么时候来个信号,所以信号的来到并不一定完全依据我们自己的想法和设计,所以,有可能在我们用sigsetjmp来设置位置缓冲之前,该信号就到来了,所以,我们应该用一个标记来保护,如果信号来了,我们进入handler,我们检察标记,看是否被置位,如果置了位,就表明位置缓冲是合法的,这样才能跳过去。在非signal的场合,由于我们基本上可以预知程序的执行流程,因此我们可以不用检查是否位置缓冲是合法的,不过检查的话更好。
下面是例子代码:
Figure 10.20. Example of signal masks, sigsetjmp, and siglongjmp#include "apue.h"
#include
#include
static void sig_usr1(int), sig_alrm(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_alrm) == SIG_ERR)
err_sys("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();
}
static void
sig_usr1(int signo)
{
time_t starttime;
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;
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: ");
}
这里的pr_mask函数的作用是,打印字符串,并将此事被block的信号打印。
sig_atomic_t类型是ISO C中定义的一种保证对其进行写操作不会被打断的类型,可见他的空间一定不会扩越页边界(那会造成缺页中断)使其操作被打断。又由于main和signal handler都在访问他,所以将其设置成volatile,保证对他的修改都协会内存,防止出现不一致性问题。
在我们以前使用setjmp和lomgjmp的时候提到过一点,即volatile型的auto变量可以保证当longjmp发生后,其状态为最新的状态,不会发生rollback,这就是因为他的存储访问都是在memory进行的,而不是register。
执行结果:
$ ./a.out & start process in background
starting main:
[1] 531 the job-control shell prints its process ID
$ kill -USR1 531 send the process SIGUSR1
starting sig_usr1: SIGUSR1
$ in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main:
just press RETURN
[1] + Done ./a.out &
//==========================
相关函数:longjmp, siglongjmp, setjmp
表头文件:#include
函数定义:int sigsetjmp(sigjmp_buf env, int savesigs)
函数说明:sigsetjmp()会保存目前堆栈环境,然后将目前的地址作一个记号,而在程序其他地方调用siglongjmp()时便会直接跳到这个记号位置,然后还原堆栈,继续程序好执行。
参数env为用来保存目前堆栈环境,一般声明为全局变量
参数savesigs若为非0则代表搁置的信号集合也会一块保存
当sigsetjmp()返回0时代表已经做好记号上,若返回非0则代表由siglongjmp()跳转回来。
返回值 :返回0代表局促存好目前的堆栈环境,随时可供siglongjmp()调用, 若返回非0值则代表由siglongjmp()返回
附加说明:setjmp()和sigsetjmp()会令程序不易令人理解,请尽量不要使用
//测试代码
#include
#include
#include
#include
#include
static jmp_buf env_alrm;
void alarm_fun()
{
printf("time out\n");
siglongjmp(env_alrm,1);
}
int test_alrm()
{
int test_value = 0;
signal(SIGALRM,alarm_fun);
if ( sigsetjmp(env_alrm, 1) != 0 )
{
alarm( 0 );
signal( SIGALRM, SIG_IGN );
return -1; /* timeout */
}
alarm(5);//5 sec;
sleep(10);//改变这里看效果
alarm(0);
signal( SIGALRM, SIG_IGN );//忽略信号
return 1;
}
int main()
{
printf("%d",test_alrm());
return 0;
}
//================================
进程部分介绍过了setjmp和longjmp函数, 这两个函数在跳转时会带信号屏蔽字跳转, 在信号处理程序(hanlder)中使用longjmp会导致后来产生的这种信号被屏蔽.
POSIX.1 也没有具体说明setjmp和longjmp对信号屏蔽字的作用, 而是定义了两个新函数: sigsetjmp和siglongjmp.
1. 原型:
可见发现sigsetjmp比setjmp多了一个参数savemask, 如果非0, 则sigsetjmp在env中保存进程的当前信号屏蔽字.
2. 实例:
还是老习惯, 用代码来验证
这段代码首先用sigaction设定SIGQUIT信号的处理函数, 然后sigsetjmp设定sigjmp_buf, 当它从siglongjmp返回时显示I'm jumped, 第一次设定完成后产生SIGQUIT信号.