关于setjmp / longjmp

时间:2011-11-01 15:52:28

标签: c linux x86 setjmp

我正在调查 setjmp / longjmp ,发现 setjmp 会保存指令,如指令指针,堆栈指针等...

然而,我没有得到的是,在调用 setjmp longjmp 之间,不能修改线程堆栈中的数据本身。在这种情况下, longjmp 不会按预期工作。

为了说清楚,例如,当 longjmp 恢复堆栈指针时,说现在堆栈指针所指向的内存中的数据与 setjmp 被叫了。这会发生吗?如果发生这种情况,我们不是有麻烦吗?

语句的意思是什么,“在调用setjmp()例程的例程返回后,可能无法调用longjmp()例程。

4 个答案:

答案 0 :(得分:6)

setjmp()/longjmp()并不意味着保存堆栈,这就是setcontext()/getcontext()的用途。

该标准指定在setjmp()setjmp()调用之间调用longjmp()的函数中定义的非易失性自动变量的值未指定{{ 1}}。由于同样的原因,您对longjmp()的调用方式也有一些限制。

答案 1 :(得分:5)

堆栈指针标记堆栈的“已使用”和“未使用”部分之间的划分。当您拨打setjmp时,所有当前的呼叫帧都在“已使用”一侧,并且在setjmp之后但在呼叫setjmp的功能返回之前发生的任何呼叫都会进行呼叫保存的堆栈指针的“未使用”侧的帧。请注意,在调用longjmp的函数返回后调用setjmp会调用未定义的行为,因此不需要考虑这种情况。

现在,某些现有调用帧中的局部变量可能会在setjmp之后被调用函数或指针修改,这就是为什么必须使用volatile的原因之一在许多情况下......

答案 2 :(得分:3)

C中的setjmp / longjmp(以下简称slj)功能很难看,其行为可能因实现而异。尽管如此,由于没有例外,在C中有时需要slj(请注意,C ++提供的异常几乎在所有方面都优于slj,并且slj与许多C ++特性交互得很厉害。)

在使用slj时,应该记住以下内容,假设例程Parent()调用例程Setter(),它调用setjmp()然后调用Jumper,后者又调用longjmp()。

  1. 代码可以合法地退出执行setjmp的范围,而不执行longjmp;但是,一旦范围退出,先前创建的jmp_buf必须被视为无效。编译器可能不会做任何事情来标记它,但任何使用它的尝试都可能导致不可预测的行为,可能包括跳转到任意地址。
  2. Jumper()中的任何局部变量都会随着对longjmp()的调用而消失,使其值无关紧要。
  3. 每当控制权返回Parent时,无论采用何种方式,Parent的局部变量都将与调用Setter时一样,除非这些变量的地址被采用并且使用这些指针进行了更改;在任何情况下,setjmp / longjmp都不会以任何方式影响它们的值。如果这些变量没有采用它们的地址,则setjmp()可能会缓存这些变量的值,而longjmp()可能会恢复它们。但是,在这种情况下,变量在缓存和恢复之间无法进行更改,因此缓存/恢复将没有可见效果。
  4. Setter中的变量可以通过setjmp()调用缓存,也可以不缓存。在调用longjmp()之后,这些变量可能具有执行setjmp()时的值,或者调用最终调用longjmp()或其任意组合的例程时的值。在至少一些C方言中,可以将这些变量声明为“易失性”以防止它们被高速缓存。

尽管setjmp / longjmp()有时可能有用,但它们也可能非常危险。在大多数情况下,没有保护错误的代码导致未定义的行为,并且在许多现实世界的情况下,不正确的使用可能会导致不良事件发生(不像某些类型的未定义行为,其中实际结果可能经常与程序员打算)。

答案 3 :(得分:0)

在下面的例子中,setjmp / longjump通过指针改变生活在main中的i的值。我永远不会在for循环中递增。有关额外的乐趣,请参阅1992年IOCCC的albert.c,http://www.ioccc.org/years-spoiler.html条目。 (我曾几次ROTFLED阅读C源代码之一...)

#include <stdio.h>
#include <setjmp.h>

jmp_buf the_state;

void helper(int *p);
int main (void)
{
int i;

for (i =0; i < 10;    ) {
    switch (setjmp (the_state) ) {
    case 0:  helper (&i) ; break;
    case 1:    printf( "Even=\t"); break;
    case 2:    printf( "Odd=\t"); break;
    default: printf( "Oops=\t"); break;
        }
    printf( "I=%d\n", i);
    }

return 0;
}
void helper(int *p)
{
*p += 1;
longjmp(the_state, 1+ *p%2);
}