- Setjmp.h
-
.
Plantilla:Lowercase setjmp.h es un encabezado definido en la biblioteca estándar de C para proporcionar "saltos no locales": control de flujo que se desvía de la llamada a subrutina habitual y la secuencia de retorno. Las funciones complementarias
setjmpylongjmpproporcionan esta funcionalidad.Un uso típico de
setjmp/longjmpes la implementación de un mecanismo de excepción que utiliza la capacidad delongjmppara restablecer el estado del programa o hilo, incluso a través de múltiples niveles de las llamadas a funciones. Un uso menos común desetjmpes crear una sintaxis similar a co-rutinas.Plantilla:Biblioteca Estandar C
Contenido
Funciones miembro
int setjmp(jmp_buf env)Establece el buffer jmp_buflocal y lo inicializa para el salto. Esta rutina[1] guarda el contexto de la llamada del programa en el búfer de contexto especificado por el argumentoenvpara su uso posterior porlongjmp. Si el retorno es de una invocación directa,setjmpdevuelve 0. Si el retorno es de una llamada alongjmp,setjmpdevuelve un valor distinto de cero.void longjmp(jmp_buf env, int value)Restaura el contexto del buffer de entorno envque se salvó por invocación de la rutina[1] en la misma invocación del programa. Invocando longjmp desde un manejador de señales anidado no-definido. El valor especificado porvalores pasado desdelongjmpasetjmp. Después de quelongjmpse completa, la ejecución del programa continúa como si la invocación correspondiente desetjmpacabara de regresar. Si elvalorpasado alongjmpes 0,setjmpse comportará como si hubiera devuelto 1;, de lo contrario, se comportará como si hubiera devuelto unvalor.setjmpguarda el contexto actual (Ej., el estado del programa) en algún punto de la ejecución del programa, en una estructura de datos específica de la plataforma (jmp_buf) que puede ser usada, en un punto más lejano de la ejecución del programa, porlongjmppara restaurar el estado del programa que fue guardado porsetjmpenjmp_buf. Este proceso puede representarse como un "salto" atras hacia el punto de la ejecución del programa dondesetjmpguardo el contexto. El (aparente) valor de retorno desetjmpindica si el control alcanzado el punto de normalidad o de una llamada alongjmp. Esto lleva a un común [[lenguaje de programación |] idioma]:if( setjmp(x) ){/* manejar longjmp(x) */}.POSIX.1 no especifica si
setjmpylongjmpguardan o restauran el conjunto actual de bloqueo de señales — si un programa cuenta con manejo de señales que deberia usarlas instrucciones POSIXsigsetjmp/siglongjmp.Tipos Miembro
jmp_bufUn tipo de array, como struct __jmp_buf_tag[1],[2] apropiado para manejar la información necesaria para restaurar el contexto desde una llamada a sistema.The C99 Rationale describes
jmp_bufas being an array type for backwards compatibility; existing code refers tojmp_bufstorage locations by name (without the&address-of operator), which is only possible for array types.[3]Advertencias y limitaciones
Cuando un "no-local goto" se ejecuta a través de setjmp / longjmp, normal "desenredo de pila" no ocurre y por lo tanto, las acciones de limpieza necesarios, tales como el cierre de descriptores de archivos, vaciar los búferes, la liberación de heap asignado de memoria, etc, no ocurrir.
Si la función en la que fue llamada setjmp regresa, ya no es posible utilizar de forma segura longjmp con el objeto jmp_buf correspondiente. Esto es así porque el marco de pila se invalida cuando se devuelve la función. Llamar a longjmp restablece el puntero de pila, que-ya que la función devuelve-apuntaría a una pila no existe y posiblemente sobrescribir / dañado frame.4 5
Del mismo modo, C99 no requiere que longjmp preservar el marco de pila actual. Esto significa que saltar a una función que se sale a través de una llamada a longjmp es undefined.6 Sin embargo, la mayoría de las implementaciones de longjmp dejar intacto el marco de pila, lo que permite setjmp y longjmp que se utiliza para saltar hacia atrás y hacia adelante entre dos o más funciones -una característica explotada para la multitarea.
En comparación con los mecanismos de lenguajes de programación de alto nivel como Python, Java, C + +, C #, e incluso pre-C lenguajes como Algol 60, la técnica del uso de setjmp / longjmp para implementar un mecanismo de excepción es aburrida. Estos lenguajes proporcionan las técnicas de manejo de excepción más potente, mientras que lenguas como el esquema, Smalltalk, y Haskell proporcionar más generales de manipulación de continuación construcciones.
Ejemplos de uso
Ejemplo simple
Este ejemplo muestra la idea básica de 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 statementif ( ! setjmp(buf) )is executed a second time.Manejo de excepciones
En este ejemplo,
setjmpis used to bracket exception handling, like 1999 ISO C standard and Single UNIX Specification by invokingsetjmpin a limited range of contexts:[4]- 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 asswitch ((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() { void *volatile 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 */ }
Multitarea cooperativa
C99 provee que
longjmpis 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 byreturnorlongjmpis undefined.[5] However, most implementations oflongjmpdo not specifically destroy local variables when performing the jump. Since the context survives until its local variables are erased, it could actually be restored bysetjmp. In many environments (such as Really Simple Threads and TinyTimbers), idioms such asif(!setjmp(child_env)) longjmp(caller_env);can allow a called function to effectively pause-and-resume at asetjmp.This is exploited by thread libraries to provide cooperative multitasking facilities without using
setcontextor other fiber facilities. Whereassetcontextis a library service which can create an execution context in heap-allocated memory and can support other services such as buffer overflow protection[cita requerida], abuse ofsetjmpis 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 ofsetcontextmay internally usesetjmpin 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, andsetcontext, as part of POSIX, is not required to be provided by C implementations, this mechanism may be portable where thesetcontextalternative 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) */ }
Véase también
- setcontext
- continuation
Referencias
- ↑ a b ISO C establece que
setjmp</ code> debe ser implementado como una macro, pero POSIX establece explícitamente que no está definido sisetjmp</ code> es una macro o una función. ↑ Este es el tipo usado por la Biblioteca GNU C, version 2.7↑ a b C99 Rationale, version 5.10, April 2003, section 7.13↑ Plantilla:Man↑ Error en la cita: El elemento<ref>no es válido; pues no hay una referencia con texto llamadaISO.2FIEC_9899:1999
Enalces Externos
- Plantilla:Man
- Exceptions in C with Longjmp and Setjmp
Categorías:- Articulos con ejemplos en código C
- Bibliotecas Cabeceras de C standard
- Control de flujo
- As the condition to an
Wikimedia foundation. 2010.