setjmp()和longjmp()
#include<setjmp.h>
int setjmp(jmp_buf envbuf)
------------------------------------------------
------------------------------------------------
宏函数setjmp()在缓冲区envbuf中保存系统堆栈里的内容,供longjmp()以后使用,setjmp()必须使用头文件setjmp.h。
调用setjmp()宏时,返回值为0,然而longjmp()把一个变原传(一个确认用于从那里返回的整形参数int status)递给setjmp(),该值(恒不为0)就是调用longjmp()后出现的setjmp()的值void longjmp(jmp_buf envbuf,int status);
** 函数longjmp()使程序在最近一次调用setjmp()处重新执行。
setjmp()和longjmp()提供了一种在函数间调转的手段,必须使用头部文件setjmp.h。
函数longjmp()通过把堆栈复位成envbuf中描述的状态进行操作,envbuf的设置是由预先调用setjmp()生成的。这样使程序的执行在setjmp()调用后的下一个语句从新开始,使计算机认为从未离开调用setjmp()的函数。从效果上看,longjmp()函数似乎“绕”过了时间和空间(内存)回到程序的原点,不必执行正常的函数返回过程。
缓冲区envbuf具有<setjmp.h>中定义的buf_jmp类型,它必须调用longjmp()前通过调用setjmp()来设置好。
值status变成setjmp()的返回值,由此确定长调转的来处。不允许的唯一值是0,0是程序直接调用函数setjmp()时由该函数返回的,不是间接通过执行函数longjmp()返回的。
longjmp()函数最常用于在一个错误发生时,从一组深层嵌套的实用程序中返回。
例子
这个例子输出“1 2 3“
#i nclude<stdio.h>
#i nclude <setjmp.h>
jmp_buf ebuf;
void f2(void);
int main(void)
{
int i;
printf("1");
i=setjmp(ebuf);
if(i==0)
{
f2();
printf("This will not be printed.");
}
printf("%d",i);
return 0;
}
void f2(void)
{
printf("2");
longjmp(ebuf,3);
}
-------------------------------------------------
sigsetjmp/siglongjmp
-------------------------------------------------
在信号处理函数调用之前,系统自动阻塞当前被处理的信号以防止接下来发生的该类信号中断信号处理函数,
这使得使用longjmp从信号处理函数返回时,出现是否恢复信号掩码的问题。有些系统恢复,有些系统不恢复,
POSIX.1对此没有指示,而是提供了另外两个函数sigsetjmp和siglongjmp。当从信号处理函数跳出时,应该使用siglongjmp函数。
int sigsetjmp( sigjmp_buf * env, int savemask );
void siglongjmp ( sigjmp_buf * env, int val );
当调用sigsetjmp时,参数savemask指示是否将当前进程的信号掩码存储在env中;
在调用siglongjmp时,若env中有之前存储的信号掩码,则恢复进程的信号掩码。
在信号处理函数中调用siglongjmp时,需要特殊的保护方案:
通常在sigsetjmp调用之前部署信号处理函数,若在sigsetjmp初始化sigjmp_buf之前,信号发生,信号处理函数中的siglongjmp被调用,这将会出错。需要提供一种保护机制,保证在sigsetjmp完成之前,信号处理函数中的siglongjmp不会被调用。ISO C提供了一种sig_automic_t可以原子的写。全局定义一个sig_atomic_t类型的数据canjmp,初始值为0,在sigsetjmp完成之后将其值修改为1,在信号处理函数里面当且仅当canjmp不为0时,才回调用siglongjmp,这样就确保了在sigsetjmp调用之后才会对siglongjmp调用。通常也将canjmp限定为volatile(它被两个线程同时访问:主线程和信号处理函数)。
longjmp有一个问题,当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动的加到进程的信号屏蔽字中。这阻止了后来产生的这种信号中断该信号处理程序。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <setjmp.h>
sigjmp_buf main_env;
int trigger_cnt = 1;
int segv_cnt = 1;
void
segv_trigger(void)
{
printf("SIGSEGV tigger %d.\n", trigger_cnt++);
int *p = NULL;
*p = 0;
}
void
segv_handler(int signum)
{
assert(signum == SIGSEGV);
printf("SIGSEGV has been catched %d times.\n", segv_cnt++);
siglongjmp(main_env, 1);
}
int
main(void)
{
struct sigaction segv_act;
volatile int longjmp_cnt;
segv_act.sa_handler = segv_handler;
sigemptyset(&segv_act.sa_mask);
segv_act.sa_flags = 0;
if (sigaction(SIGSEGV, &segv_act, 0) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
longjmp_cnt = 1;
if (sigsetjmp(main_env, 1) != 0) {
printf("Long jump %d.\n", longjmp_cnt++);
if (longjmp_cnt > 3)
exit(EXIT_SUCCESS);
}
segv_trigger();
/**//* UNREACHABLE */
exit(EXIT_SUCCESS);
}