setjmp的使用

以前对于C语言的setjmp和longjmp从来都是知道有这么个函数, 但是不知道什么情况下要使用, 甚至于不知道setjmp的实现机制是什么样子的.

这次在实现coroutine的过程中虽然没有使用setjmp来实现, 但是由于setjmp来实现coroutine的所有操作都是可以在用户态进行的, 因此顺便研究了一下setjmp的实现机制.

与我猜测大致一样, 在call stack上来讲setjmp一定先于或等于longjmp处于的位置, 这样其实longjmp所做的不过就是将当前寄存器上下文恢复成setjmp函数保存的值即可. 调用longjmp的函数与调用setjmp的函数共用一个栈空间, 因此longjmp恢复完寄存器后造成的结果就是恢复寄存器上下文并将栈空间释放到setjmp位于的地方.

那么这就很好解释为什么不能将setjmp使用函数再封装一层了, 假设使用函数封装setjmp代码如下:

int try(jmp_buf jmp)
{
int err;
err = setjmp(jmp);
printf("%d\n", err);
return err;
}

void execption(jmp_buff j, int err)
{
return longjmp(j, err)
}

int main()
{
jmp_buff j;
try(j);
execption(j, 2);
}

如上代码其实try函数与execption共用部分栈空间, 当longjmp回到try函数时, 即使用寄存器被恢复了, 但是事实上try函数的栈结构已经被破坏了, 此时longjmp之后try函数的行为是无法预料的.

------------------------
|        main          |
|-----------------------
|     try |  excption  |
|---------|            |
|---------|------------|

在使用setjmp时一定要注意栈是否被覆盖的问题, 即调用setjmp时所保存的esp的信息直到调用前longjmp之前, 不应该会出更有比setjmp保存esp时更多的空闲空间(如栈是自顶向下增加, 则setjmp到longjmp之间出现的esp的值绝不能大于setjmp保存的esp的值.

发表评论

6 × = six