您的位置:首页 > 编程语言 > C语言/C++

C语言学习:非局部跳转setjmp.h中的几个重要函数

2017-07-27 22:22 295 查看
C语言学习:非局部跳转setjmp.h中的几个重要函数

简介:

在C语言中,我们知道在一个函数的内部是可以通过goto来跳转到我们实现声明标志的地方的,但是这种跳转往往是基于函数内部实现的,而C标准库为我们提供的setjmp中,提供了setjmp,longjmp等函数,就是为了实现非局部跳转和goto的功能的。也是C语言实现异常处理的一种有效方案。

提示:

博客:章飞_906285288

博客地址:http://blog.csdn.net/qq_29924041

setjmp中的函数介绍:

#include <setjmp.h>
int setjmp(jmp_buf env);


//setjmp()宏把当前状态信息保存到env中,供以后longjmp()恢复状态信息时使用。如果是直接调用setjmp(),那么返回值为0;如果是由于调用longjmp()而调用setjmp(),那么返回值非0。setjmp()只能在某些特定情况下调用,如在if语句、 switch语句及循环语句的条件测试部分以及一些简单的关系表达式中

void longjmp(jmp_buf env, int val);


//longjmp()用于恢复由最近一次调用setjmp()时保存到env的状态信息。当它执行完时,程序就象setjmp()刚刚执行完并返回非0值val那样继续执行。包含setjmp()宏调用的函数一定不能已经终止。所有可访问的对象的值都与调用longjmp()时相同,唯一的例外是,那些调用setjmp()宏的函数中的非volatile自动变量如果在调用setjmp()后有了改变,那么就变成未定义的。

从代码的角度来展示一下

/*
* ===========================================================================
*
*       Filename:  jmp.cpp
*    Description:
*        Version:  1.0
*        Created:  2017年07月25日 23时27分11秒
*       Revision:  none
*       Compiler:  gcc
*         Author:   (),
*        Company:
*
* ===========================================================================
*/

#include<iostream>
//标准C语言库中的的非局部跳转
#include<setjmp.h>
using namespace::std;

static jmp_buf static_buf;  //用来存放处理器的上下文,主要用于跳转

void jmp(){
//调用longjmp之后,会载入static_buf的处理器信息,第二个参数作为setjmp的返回值
cout << "before calling" <<endl;
longjmp(static_buf,12); //12是错误的返回值,用来做差异处理
cout << "after calling "<<endl; //此句是不会执行的
}

int main(int argc,char* argv[]){
int ret = 0;
//将处理器信息保存到stati_buf中来,并且返回0,相当于一个标记
if((ret = setjmp(static_buf)) == 0){
cout << "first calling" << endl;
jmp();
} else {//出现错误
if(ret == 12){
cout <<" error"<< endl;
}
}

return 0;
}


执行结果:

first calling
before calling
error


从上面可以看到,显示去进行注册,当longjmp发生的时候,这个函数会去第二次执行判断。这个时候走的就是else部分。

还有两个重要的函数siglongjmp和sigsetjmp:

在看这两个函数之前,可以看到这两个函数都是sig开头的,而在posix标准中,sig一般都是signal信号的意思,那这两个函数务必是于signal信号相关的。事实上也确实是。

先了解下什么是信号屏蔽字,信号屏蔽字就是进程中被阻塞的信号集, 这些信号不能发送给该进程, 它们在该进程中被”屏蔽”了. 后面我们会提到, 实际上它们是被阻塞了.也就是信号屏蔽了。

sigsetjmp和siglongjmp出现的意义是什么???在上述的sigjmp函数和longjmp函数中会有这样一个比较明显的弊端,就是当捕捉到一个信号,进入信号执行函数后,仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。会默认将此信号加入到信号屏蔽字中,这就会导致后来的这种信号对于此类信号中断该信号处理程序。而sigsetjmp和siglongjmp恰恰是为了解决这样一种场景的发生的

void siglongjmp(sigjmp_buf env, int val);

int sigsetjmp(sigjmp_buf env, int savesigs);
//如果是直接调用则返回0,如果是由于siglongjmp来调用,则返回的是非0.


如果savemask非0,则sigsetjmp在env中保存进程的当前信号屏蔽字,当用siglongjmp时,如果带 非0 savemask的sigsetjmp调用已经保存了env,则siglongjmp从其中恢复保存的信号屏蔽字

sigsetjmp()会保存目前堆栈环境,然后将目前的地址作一个记号,

而在程序其他地方调用siglongjmp()时便会直接跳到这个记号位置,然后还原堆栈,继续程序的执行。

参数env为用来保存目前堆栈环境,一般声明为全局变量

/*
* ===========================================================================
*
*       Filename:  sigjmp.cpp
*    Description:
*        Version:  1.0
*        Created:  2017年07月27日 21时56分33秒
*       Revision:  none
*       Compiler:  gcc
*         Author:   (),
*        Company:
*
* ===========================================================================
*/

#include<iostream>
#include<signal.h>
#include<setjmp.h>
using namespace::std;

static sigjmp_buf buf;
void sig_handler(int signo){

cout << "before jmp" <<endl;
siglongjmp(buf,10); //在收到信号的时候,调用siglongjmp,恢复堆栈信息
}

int main(int argc,char *argv[]){
if(signal(SIGUSR2,sig_handler) == SIG_ERR){ //注册信号处理函数
cout << "signal error"<< endl;
}

int ret;
ret = sigsetjmp(buf,10);  //保存当前的堆栈环境,将目前的地址做一个记号
switch(ret){
case 0:
cout << "first jmp" << endl;
break;
case 10:
cout << "after jmp" << endl;
break;
default:

break;
}

while(1);
return 0;
}


这个程序中,每次恢复栈之后,如果继续kill -12 xxx这个进程的话,依旧会持续的收到,这是因为其从信号屏蔽集中已经恢复。

注意:

在信号处理函数中的非局部跳转,尽量使用的是siglongjmp和sigsetjmp函数。使从信号处理函数中恢复到主函数中去
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息