问题
I was investigating setjmp/longjmp and found out that setjmp saves registers such as instruction pointer, stack pointer etc...
However what I don't get here is that, can't the data in the stack of the thread itself be modified between the call to setjmp and longjmp. In that case, wouldn't longjmp not work as expected.
To make it clear, for example, when longjmp restores the stack pointer, say the data in the memory the stack pointer is pointing now is not the same as was when setjmp was called. Can this happen? And if that happens, aren't we in trouble?
Also what is meant by the statement, "The longjmp() routines may not be called after the routine which called the setjmp() routines returns."
回答1:
The stack pointer marks the division between the "used" and "unused" portions of the stack. When you call setjmp
, all current call frames are on the "used" side, and any calls that take place after setjmp
, but before the function which called setjmp
returns, have their call frames on the "unused" side of the saved stack pointer. Note that calling longjmp
after the function which called setjmp
has returned invokes undefined behavior, so that case does not need to be considered.
Now, it's possible that local variables in some of the existing call frames are modified after setjmp
, either by the calling function or through pointers, and this is one reason why it's necessary to use volatile
in many cases...
回答2:
setjmp()/longjmp()
are not meant to save the stack, that's what setcontext()/getcontext()
are for.
The standard specifies that the value of non-volatile automatic variables defined in the function that calls setjmp()
that are changed between the setjmp()
and the longjmp()
calls are unspecified after a longjmp()
. There are also some restrictions on how you call setjmp()
for this same reason.
回答3:
The setjmp/longjmp (hereafter slj) feature in C is ugly, and its behavior may vary between implementations. Nonetheless, given the absence of exceptions, slj is sometimes necessary in C (note that C++ provides exceptions which are in almost every way superior to slj, and that slj interacts badly with many C++ features).
In using slj, one should bear in mind the following, assuming routine Parent() calls routine Setter(), which calls setjmp() and then calls Jumper, which in turn calls longjmp().
- Code may legally exit the scope in which a setjmp is performed without a longjmp having been executed; as soon as the scope exits, however, the previously-created jmp_buf must be regarded as invalid. The compiler probably won't do anything to mark it as such, but any attempt to use it may result in unpredictable behavior, likely including a jump to an arbitrary address.
- Any local variables in Jumper() will evaporate with the call to longjmp(), rendering their values irrelevant.
- Whenever control returns to Parent, via whatever means, Parent's local variables will be as they were when it called Setter, unless such variables had their addresses taken and were changed using such pointers; in any case, setjmp/longjmp will not affect their values in any way. If such variables do not have their addresses taken, it is possible that setjmp() may cache the values of such variables and longjmp() may restore them. In that scenario, however, there would be no way for the variables to change between when they are cached and when they are restored, so the cache/restore would have no visible effect.
- The variables in Setter may or may not be cached by setjmp() call. After a longjmp() call, such variables may have the value they had when setjmp() was performed, or the values they had when it called the routine which ultimately called longjmp(), or any combination thereof. In at least some C dialects, such variables may be declared "volatile" to prevent them from being cached.
Although setjmp/longjmp() can sometimes be useful, they can also be very dangerous. There is in most cases no protection errant code causing Undefined Behavior, and in many real-world scenarios, improper usage is likely to cause bad things to happen (unlike some kinds of Undefined Behavior, where the actual outcome may often line up with what the programmer intended).
回答4:
In the example below, setjmp / longjump alters the value of i, which lives in main, via a pointer. I is never incremented in the for loop. For extra fun see the entry albert.c , http://www.ioccc.org/years-spoiler.html winner of the 1992 IOCCC. (one of the few times I ever ROTFLed reading a C source ...)
#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);
}
来源:https://stackoverflow.com/questions/7969075/about-setjmp-longjmp