关于setjmp / longjmp

我正在调查setjmp / longjmp ,发现setjmp保存了指令指针,堆栈指针等寄存器。

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

为了说清楚,例如,当longjmp恢复堆栈指针时,说出堆栈指针现在指向的内存中的数据与setjmp被调用时不同。 这可以发生吗? 如果那样的话,我们不是有麻烦吗?

此外,声明“ longjmp()例程可能不会在调用setjmp()例程的例程返回后调用”。

堆栈指针标记堆栈的“已用”和“未用”部分之间的区分。 当你调用setjmp ,所有当前的调用框架都在“used”一侧,而任何在setjmp之后但在调用setjmp的函数之前发生的调用,都会在保存的堆栈指针的“未使用” 。 请注意,在调用setjmp返回的函数之后调用longjmp调用未定义的行为,因此不需要考虑这种情况。

现在,一些现有的调用框架中的局部变量可能会在setjmp之后被调用函数或通过指针来修改,这就是为什么在很多情况下需要使用volatile原因之一。

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

该标准指定调用setjmp()函数中定义的非易失性自动变量在setjmp()longjmp()调用之间更改的值在longjmp()之后未指定。 由于同样的原因,你也有一些限制你调用setjmp()

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语言中,这样的变量可能被声明为“volatile”,以防止它们被缓存。

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

这很好地解释了事情。

在下面的例子中,setjmp / longjump通过一个指针改变了i的值,它位于main中。 我永远不会在for循环中增加。 如需额外的乐趣,请参阅1992年IOCCC的获奖者albert.c, http://www.ioccc.org/years-spoiler.html 。 (我曾几次阅读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); }