c语言中setjmp与longjmp(2)

如何实现异常处理
首先设置一个跳转点(setjmp() 函数可以实现这一功能),然后在其后的代码中任意地方调用 longjmp() 跳转回这个跳转点上,以此来实现当发生异常时,转到处理异常的程序上,在其后的介绍中将介绍如何实现。 setjmp() 为跳转返回保存现场并为异常提供处理程序,longjmp() 则进行跳转(抛出异常),setjmp() 与 longjmp() 可以在函数间进行跳转,这就像一个全局的 goto 语句,可以跨函数跳转。

jmp_buf 异常结构

  使用 setjmp() 及 longjmp() 函数前,需要先认识一下 jmp_buf 异常结构。jmp_buf 将使用在 setjmp() 函数中,用于保存当前程序现场(保存当前需要用到的寄存器的值),jmp_buf 结构在 setjmp.h 文件内声明:

        typedef struct
        {
                unsigned j_sp;  // 堆栈指针寄存器
                unsigned j_ss;  // 堆栈段
                unsigned j_flag;  // 标志寄存器
                unsigned j_cs;  // 代码段
                unsigned j_ip;  // 指令指针寄存器
                unsigned j_bp; // 基址指针
                unsigned j_di;  // 目的指针
                unsigned j_es; // 附加段
                unsigned j_si;  // 源变址
                unsigned j_ds; // 数据段
        } jmp_buf;

        jmp_buf 结构存放了程序当前寄存器的值,以确保使用 longjmp() 后可以跳回到该执行点上继续执行。

 
int setjmp(jmp_buf envbuf)

void longjmp(jmp_buf envbuf,int status);

setjmp() 与 longjmp() 函数都使用了 jmp_buf 结构作为形参,它们的调用关系是这样的:
        首先调用 setjmp() 函数来初始化 jmp_buf 结构变量 jmpb,将当前CPU中的大部分影响到程序执行的积存器存入 jmpb,为 longjmp() 函数提供跳转,setjmp() 函数是一个有趣的函数,它能返回两次,它应该是所有库函数中唯一一个能返回两次的函数,第一次是初始化时,返回零,第二次遇到 longjmp() 函数调用后,longjmp() 函数使 setjmp() 函数发生第二次返回,返回值由 longjmp() 的第二个参数给出(整型,这时不应该再返回零)。
        在使用 setjmp() 初始化 jmpb 后,可以其后的程序中任意地方使用 longjmp() 函数跳转会 setjmp() 函数的位置,longjmp() 的第一个参数便是 setjmp() 初始化的 jmpb,若想跳转回刚才设置的 setjmp() 处,则 longjmp() 函数的第一个参数是 setjmp() 所初始化的 jmpb 这个异常,这也说明一件事,即 jmpb 这个异常,一般需要定义为全局变量,否则,若是局部变量,当跨函数调用时就几乎无法使用(除非每次遇到函数调用都将 jmpb 以参数传递,然而明显地,是不值得这样做的);longjmp() 函数的第二个参数是传给 setjmp() 的第二次返回值。值status变成setjmp()的返回值,由此确定长调转的来处。不允许的唯一值是0,0是程序直接调用函数setjmp()时由该函数返回的,不是间接通过执行函数longjmp()返回的。如果longjmp传送的value参数值为0,那么实际上被setjmp返回的值是1.


实现机理
setjmp()保存其调用返回点的ebx, esi, edi, ebp, esp, eip,对于sigsetjmp(), 还保存当前的信号屏蔽字, longjmp恢复这些寄存器及信号屏蔽字,同时传递一个返回值。longjmp只是使CPU的状态和setjmp时的状态一致,相同的CPU初始状态执行相同的代码并不意味着产生相同的结果,setjmp, longjmp并不关心内存变量的变化,而只关心CPU的状态,内存变量的确定性应该根据上下文来灵活处理.

如果将函数之间的调用关系看成一种树型结构, 将可执行文件的入口函数看成它的最内层节点(根节点), 那么setjmp提供了标记节点的方法, longjmp提供了从外层节点直接返回到更内层节点的方法, 在同一层节点之间或者从内层节点到外层节点的longjmp是没有意义的, 因为目标节点在此时根本不存在.

 

具体见http://www.upsdn.net/html/2004-11/47.html

 

还有一篇介绍的文章比较深入http://blog.codingnow.com/2010/05/setjmp.html

其中有coroutine的概念,c++中RAII的概念。

 

一位仁兄写的调度程序:
#include<setjump.h>
jmp_buf jumper0,jumper1,jumper2,jumper3;
void Task0()
{
    static int n=0;
    if(setjmp(jumper0)>0)
    {
        while(1)
        {
             if(setjmp(jumper0)==0) longjump(jumper1,1);  //任务切换
             n++;   //一段任务代码
             if(setjmp(jumper0)==0) longjump(jumper1,1);  //任务切换
             n++;   //一段任务代码
        }
    }

}
void Task1()
{
    static int n=0;
    if(setjmp(jumper1)>0)
    {
        while(1)
        {
             if(setjmp(jumper1)==0) longjump(jumper2,1);
             n++;
             if(setjmp(jumper1)==0) longjump(jumper2,1);
             n++;
        }
    }

}
void Task2()
{
    static int n=0;
    if(setjmp(jumper2)>0)
    {
        while(1)
        {
             if(setjmp(jumper2)==0) longjump(jumper3,1);
             n++;
             if(setjmp(jumper2)==0) longjump(jumper3,1);
             n++;
        }
    }

}
void Task3()
{
    static int n=0;
    if(setjmp(jumper3)>0)
    {
        while(1)
        {
             if(setjmp(jumper3)==0) longjump(jumper0,1);
             n++;
             if(setjmp(jumper3)==0) longjump(jumper0,1);
             n++;
        }
    }

}
void InitTask()
{
    Task0();
    Task1();
    Task2();
    Task3();
}
main()
{
    InitTask();
    longjmp(jumper0,1)
}

//以上代码在keil c中调试通过
//非占先式任务切换
//任务内变量必须是静态的,子程序不用
//任务内不要用寄存器变量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值