C异常处理机制:setjmp和longjmp
2011-03-08 23:35
405 查看
http://en.wikipedia.org/wiki/Setjmp.h
setjmp.h is a header defined in the C standard library to provide "non-local jumps": control flow that deviates from the usual subroutine call and return sequence. The complementary functions
A typical use of
POSIX.1 does not specify whether
The C99 Rationale describes
If the function in which
Similarly, C99 does not require that
Compared to mechanisms in higher-level programming languages such as Python, Java, C++, C#, and even pre-C languages such as Algol 60, the technique of using
When executed, the above program will output:
Notice that although the
As the condition to an
As above in conjunction with a single
As a statement (with the return value unused)
Following these rules can make it easier for the implementation to create the environment buffer, which can be a sensitive operation.[3] More general use of
This is exploited by thread libraries to provide cooperative multitasking facilities without using
Considering that
Since no exception will be generated upon overflow of one of the multiple stacks in such a mechanism, it is essential to overestimate the space required for each context, including the one containing
continuation
^ This is the type used by the GNU C Library, version 2.7
^ a b C99 Rationale, version 5.10, April 2003, section 7.13
^ CS360 Lecture Notes — Setjmp and Longjmp
^ setjmp(3)
^ a b ISO/IEC 9899:1999, 2005, 7.13.2.1:2 and footnote 211
^
Exceptions in C with Longjmp and Setjmp
setjmp.h is a header defined in the C standard library to provide "non-local jumps": control flow that deviates from the usual subroutine call and return sequence. The complementary functions
setjmpand
longjmpprovide this functionality.
A typical use of
setjmp/
longjmpis implementation of an exception mechanism that utilizes the ability of
longjmpto reestablish program (or thread?) state, even across multiple levels of function calls. A less common use of
setjmpis to create syntax similar to coroutines.
Contents[hide]1 Member functions 2 Member types 3 Caveats and limitations 4 Example usage 4.1 Simple example 4.2 Exception handling 4.3 Cooperative multitasking 5 See also 6 References 7 External links |
[edit]Member functions
int setjmp(jmp_buf env) | Sets up the local jmp_bufbuffer and initializes it for the jump. This routine[1] saves the program's calling environment in the environment buffer specified by the envargument for later use by longjmp. If the return is from a direct invocation, setjmpreturns 0. If the return is from a call to longjmp, setjmpreturns a nonzero value. |
void longjmp(jmp_buf env, int value) | Restores the context of the environment buffer envthat was saved by invocation of the setjmproutine[1] in the same invocation of the program. Invoking longjmp from a nested signal handler isundefined. The value specified by valueis passed from longjmpto setjmp. After longjmpis completed, program execution continues as if the corresponding invocation of setjmphad just returned. If the valuepassed to longjmpis 0, setjmpwill behave as if it had returned 1; otherwise, it will behave as if it had returned value. |
setjmpsaves the current environment (i.e., the program state) at some point of program execution, into a platform-specific data structure (
jmp_buf) which can be used, at some later point of program execution, by
longjmpto restore the program state to that which was saved by
setjmpinto
jmp_buf. This process can be imagined to be a "jump" back to the point of program execution where
setjmpsaved the environment. The (apparent) return value from
setjmpindicates whether control reached that point normally or from a call to
longjmp. This leads to a common idiom:
if( setjmp(x) ){/* handle longjmp(x) */}.
POSIX.1 does not specify whether
setjmpand
longjmpsave or restore the current set of blocked signals — if a program employs signal handling it should use POSIX's
sigsetjmp/
siglongjmp.
[edit]Member types
jmp_buf | An array type, such as struct __jmp_buf_tag[1][2], suitable for holding the information needed to restore a calling environment. |
jmp_bufas being an array type for backwards compatibility; existing code refers to
jmp_bufstorage locations by name (without the
&address-of operator), which is only possible for array types.[3]
[edit]Caveats and limitations
When a "non-local goto" is executed viasetjmp/
longjmp, normal "stack unwinding" does not occur and therefore, any required cleanup actions such as closing file descriptors, flushing buffers, freeing heap-allocated memory, etc., do not occur.
If the function in which
setjmpwas called returns, it is no longer possible to safely use
longjmpwith the corresponding
jmp_bufobject. This is because the stack frame is invalidated when the function returns. Calling
longjmprestores thestack pointer, which—because the function returned—would point to a non-existent and potentially overwritten/corrupted stack frame.[4][5]
Similarly, C99 does not require that
longjmppreserve the current stack frame. This means that jumping into a function which was exited via a call to
longjmpis undefined.[6] However, most implementations of
longjmpleave the stack frame intact, allowing
setjmpand
longjmpto be used to jump back-and-forth between two or more functions—a feature exploited for multitasking.
Compared to mechanisms in higher-level programming languages such as Python, Java, C++, C#, and even pre-C languages such as Algol 60, the technique of using
setjmp/
longjmpto implement an exception mechanism is uninspiring. These languages provide more powerful exception handling techniques, while languages such as Scheme, Smalltalk, and Haskell provide even more general continuation-handling constructs.
[edit]Example usage
[edit]Simple example
This example shows the basic idea of setjmp. Main calls first, which in turn calls second. The "second" function jumps back into main, skipping "first"'s print statement.#include <stdio.h> #include <setjmp.h> static jmp_buf buf; void second(void) { printf("second/n"); // prints longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 } void first(void) { second(); printf("first/n"); // does not print } int main() { if ( ! setjmp(buf) ) { first(); // when executed, setjmp returns 0 } else { // when longjmp jumps back, setjmp returns 1 printf("main/n"); // prints } return 0; }
When executed, the above program will output:
second main
Notice that although the
first()subroutine gets called, "
first" never is printed. "
main" gets printed as the conditional statement
if ( ! setjmp(buf) )is executed a second time.
[edit]Exception handling
In this example,setjmpis used to bracket exception handling, like
tryin some other languages. The call to
longjmpis analogous to a
throwstatement, allowing an exception to return an error status directly to the
setjmp. The following code adheres to the 1999 ISO C standard and Single UNIX Specification by invoking
setjmpin a limited range of contexts:[7]
As the condition to an
if,
switchor iteration statement
As above in conjunction with a single
!or comparison with an integer constant
As a statement (with the return value unused)
Following these rules can make it easier for the implementation to create the environment buffer, which can be a sensitive operation.[3] More general use of
setjmpcan cause undefined behaviour, such as corruption of local variables; conforming compilers and environments are not required to protect or even warn against such usage. However, slightly more sophisticated idioms such as
switch ((exception_type = setjmp(env))) { }are common in literature and practice, and remain relatively portable. A simple conforming methodology is presented below, where an additional variable is maintained along with the state buffer. This variable could be elaborated into a structure incorporating the buffer itself.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <setjmp.h> void first(void); void second(void); /* This program's output is: calling first calling second entering second second failed with type 3 exception; remapping to type 1. first failed, exception type 1 */ /* Use a file scoped static variable for the exception stack so we can access * it anywhere within this translation unit. */ static jmp_buf exception_env; static int exception_type; int main() { volatile void *mem_buffer; mem_buffer = NULL; if (setjmp(exception_env)) { /* if we get here there was an exception */ printf("first failed, exception type %d/n", exception_type); } else { /* Run code that may signal failure via longjmp. */ printf("calling first/n"); first(); mem_buffer = malloc(300); /* allocate a resource */ printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */ } if (mem_buffer) free((void*) mem_buffer); /* carefully deallocate resource */ return 0; } void first(void) { jmp_buf my_env; printf("calling second/n"); memcpy(my_env, exception_env, sizeof(jmp_buf)); switch (setjmp(exception_env)) { case 3: /* if we get here there was an exception. */ printf("second failed with type 3 exception; remapping to type 1./n"); exception_type = 1; default: /* fall through */ memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */ longjmp(exception_env, exception_type); /* continue handling the exception */ case 0: /* normal, desired operation */ second(); printf("second succeeded/n"); /* not reached */ } memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */ } void second(void) { printf("entering second/n" ); /* reached */ exception_type = 3; longjmp(exception_env, exception_type); /* declare that the program has failed */ printf("leaving second/n"); /* not reached */ }
[edit]Cooperative multitasking
C99 provides thatlongjmpis guaranteed to work only when the destination is a calling function, i.e., that the destination scope is guaranteed to be intact. Jumping to a function that has already terminated by
returnor
longjmpis undefined.[6] However, most implementations of
longjmpdo not specifically destroy local variables when performing the jump. Since the context survives until its local variables are erased, it could actually be restored by
setjmp. In many environments (such as Really Simple Threads and TinyTimbers), idioms such as
if(!setjmp(child_env)) longjmp(caller_env);can allow a called function to effectively pause-and-resume at a
setjmp.
This is exploited by thread libraries to provide cooperative multitasking facilities without using
setcontextor other fiber facilities. Whereas
setcontextis a library service which can create an execution context in heap-allocated memory and can support other services such as buffer overflow protection[citation needed], abuse of
setjmpis implemented by the programmer, who may reserve memory on the stack and fail to notify the library or operating system of the new operating context. On the other hand, a library's implementation of
setcontextmay internally use
setjmpin a fashion similar to this example to save and restore a context, after it has been initialised somehow.
Considering that
setjmpto a child function will generally work unless sabotaged, and
setcontext, as part of POSIX, is not required to be provided by C implementations, this mechanism may be portable where the
setcontextalternative fails.
Since no exception will be generated upon overflow of one of the multiple stacks in such a mechanism, it is essential to overestimate the space required for each context, including the one containing
main()and including space for any signal handlers that might interrupt regular execution. Exceeding the allocated space will corrupt the other contexts, usually with the outermost functions first. Unfortunately, systems requiring this kind of programming strategy are often also small ones with limited resources.
#include <setjmp.h> #include <stdio.h> jmp_buf mainTask, childTask; void call_with_cushion(void); void child(void); int main(void) { if (!setjmp(mainTask)) { call_with_cushion(); /* child never returns */ /* yield */ } /* execution resumes after this "}" after first time that child yields */ for (;;) { printf("Parent/n"); if (!setjmp(mainTask)) { longjmp(childTask, 1); /* yield - note that this is undefined under C99 */ } } } void call_with_cushion (void) { char space[1000]; /* Reserve enough space for main to run */ space[999] = 1; /* Do not optimize array out of existence */ child(); } void child (void) { for (;;) { printf("Child loop begin/n"); if (!setjmp(childTask)) longjmp(mainTask, 1); /* yield - invalidates childTask in C99 */ printf("Child loop end/n"); if (!setjmp(childTask)) longjmp(mainTask, 1); /* yield - invalidates childTask in C99 */ } /* Don't return. Instead we should set a flag to indicate that main() should stop yielding to us and then longjmp(mainTask, 1) */ }
[edit]See also
setcontextcontinuation
[edit]References
^ a b ISO C states thatsetjmpmust be implemented as a macro, but POSIX explicitly states that it is undefined whether
setjmpis a macro or a function.
^ This is the type used by the GNU C Library, version 2.7
^ a b C99 Rationale, version 5.10, April 2003, section 7.13
^ CS360 Lecture Notes — Setjmp and Longjmp
^ setjmp(3)
^ a b ISO/IEC 9899:1999, 2005, 7.13.2.1:2 and footnote 211
^
setjmp: set jump point for a non-local goto – System Interfaces Reference, The Single UNIX® Specification, Issue 7 from The Open Group
[edit]External links
setjmp(3): save stack context for non-local goto – Linux Library Functions Manual
Exceptions in C with Longjmp and Setjmp
相关文章推荐
- C语言异常处理机制, setjmp() and longjmp()
- C异常处理机制:setjmp和longjmp
- 全面了解setjmp与longjmp(C语言异常处理机制)
- C异常处理机制:setjmp和longjmp(转)
- 全面了解setjmp与longjmp(C语言异常处理机制)
- C语言中一种更优雅的异常处理机制 - setjmp/longjmp
- C语言利用setjmp/longjmp实现模仿C++的层次异常处理机制
- C异常处理机制:setjmp和longjmp
- C语言异常处理——setjmp & longjmp
- c语言中的异常处理,setjmp 与 longjmp函数
- C语言下的setjmp longjmp(C 语言异常处理)
- C语言异常处理机制--setjmp的返回值
- Linux C 异常处理:非局部goto jmp_buf/setjmp()/longjmp()
- setjmp和longjmp处理异常
- c语言异常处理之longjmp 和 setjmp
- C语言中利用setjmp和longjmp做异常处理
- C语言实现异常机制setjmp() longjmp()
- UNIX笔记--(longjmp ,setjmp)异常处理
- C语言中利用setjmp和longjmp做异常处理
- C语言异常处理之setjmp、longjmp的一个小例程