非本地跳转
一种用户级的异常控制流形式。将控制直接从一个函数转移到另一个当前正在执行的函数。不需要正常的调用-返回序列(入栈--出栈)
(ps: 系统调用 为异常中陷阱的主要表现形式,
参数都是
通过通用寄存器而不是栈来传递的)
重要应用:从一个深层嵌套的函数调用中 立即返回。
相关函数:setjmp, longjmp
表头文件:#include <setjmp.h>
函数定义:int setjmp(jmp_buf env)
void longjmp(jmp_buf env, int retval);
函数说明:setjmp在env缓存区中保存当前 调用环境(程序计数器、栈指针等) ,以供后面的longjmp使用,并返回0(即第一次设置成功后 返回0,随后由longjmp触发返回后返回longjmp中的参数retval).
longjmp从env中恢复调用环境,然后触发一个从最近一次初始化env的setjmp调用的返回。然后setjmp返回,返回值为longjmp中设置的返回值retval.
setjmp一次调用,多次返回。
参数env为用来保存目前堆栈环境,一般声明为
全局变量
示例:
#include "csapp.h" jmp_buf buf; int error1 = 0; int error2 = 1; void foo(void), bar(void); int main() { int rc; rc = setjmp(buf); if(0 == rc) foo(); else if(1 == rc) printf("foo error\n"); else if(2 == rc) printf("bar error\n"); else printf("other error\n"); return 0; } void foo(void) { if(error1) longjmp(buf, 1); bar(); } void bar(void) { if(error2) longjmp(buf, 2); }
运行结果:bar error
另一个应用:使一个处理信号程序分支跳转到一个特殊的代码位置,而不是 返回到被信号到达中断了的指令的位置。
相关函数:sigsetjmp, siglongjmp
表头文件:#include <setjmp.h>
函数定义:int sigsetjmp(sigjmp_buf env, int savesigs)
void siglongjmp(jmp_buf env, int retval);
函数说明:sigsetjmp()会保存目前堆栈环境,然后将目前的地址作一个记号,而在程序其他地方调用siglongjmp()时便会直接跳到这个记号位置,然后还原堆栈,继续程序好执行。
参数env为用来保存目前堆栈环境,一般声明为全局变量
参数savesigs若为非0则代表搁置的信号集合也会一块保存
当sigsetjmp()返回0时代表已经做好记号上,若返回非0则代表由siglongjmp()跳转回来。
返回值 :返回0代表已经保存好目前的堆栈环境,随时可供siglongjmp()调用, 若返回非0值则代表由siglongjmp()返回
示例:
#include "csapp.h" sigjmp_buf buf; void handler(int sig) { siglongjmp(buf, 2); } int main() { int rc; Signal(SIGINT, handler); rc = sigsetjmp(buf, 1); if(0 == rc) printf("starting\n"); else if(1 == rc) printf("return 1\n"); else if(2 == rc) printf("return 2\n"); else printf("return other\n"); while(1) { printf("processing\n"); sleep(2); } return 0; }
测试:ctrl+c 产生中断信号。
信号处理函数 中 根据siglongjmp中retval, 触发 sigsetjmp调用,并返回相应的 retval
测试结果:
starting
processing
processing
return 2 //ctrl+c
processing
processing
processing
processing
return 2 //ctrl+c
processing
....
注:csapp.h csapp.c可网上搜索并下载