11

我正在调查setjmp/longjmp并发现setjmp保存了诸如指令指针、堆栈指针等寄存器...

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

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

还有这句话的意思是,“在调用 setjmp() 例程的例程返回之后,可能不会调用 longjmp() 例程。

4

4 回答 4

6

setjmp()/longjmp()不是为了保存堆栈,这就是setcontext()/getcontext()目的。

该标准规定,在函数中定义的非易失性自动变量的值在和调用setjmp()之间发生变化,在. 出于同样的原因,您如何打电话也有一些限制。setjmp()longjmp()longjmp()setjmp()

于 2011-11-01T16:10:51.907 回答
6

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

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

于 2011-11-01T16:16:59.523 回答
2

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

在使用 slj 时,应牢记以下几点,假设例程 Parent() 调用例程 Setter(),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() 有时很有用,但它们也可能非常危险。在大多数情况下,没有导致未定义行为的保护错误代码,并且在许多实际场景中,不正确的使用可能会导致坏事发生(不像某些类型的未定义行为,实际结果可能经常与程序员打算)。

于 2011-11-01T16:28:14.623 回答
0

在下面的示例中,setjmp / longjump 通过指针更改位于 main 中的 i 的值。I 永远不会在 for 循环中递增。如需更多乐趣,请参阅 albert.c 条目,http://www.ioccc.org/years-spoiler.html 1992 年 IOCCC 获胜者。(我曾经 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);
}
于 2011-11-01T16:28:25.653 回答