¿Alguien puede explicarme dónde exactamente setjmp()
y las longjmp()
funciones se pueden usar prácticamente en la programación integrada? Sé que estos son para el manejo de errores. Pero me gustaría conocer algunos casos de uso.
98
longjmp()
para salir de un manejador de señales, especialmente cosas como aBUS ERROR
. Esta señal no suele reiniciarse. Es posible que una aplicación incorporada desee manejar este caso por motivos de seguridad y funcionamiento robusto.setjmp
entre BSD y Linux, consulte "Timing setjmp y el placer de los estándares" , que sugiere usarsigsetjmp
.Respuestas:
Manejo de errores
Suponga que hay un error en el fondo de una función anidada en muchas otras funciones y el manejo de errores solo tiene sentido en la función de nivel superior.
Sería muy tedioso e incómodo si todas las funciones intermedias tuvieran que regresar normalmente y evaluar los valores devueltos o una variable de error global para determinar que el procesamiento posterior no tiene sentido o incluso sería malo.
Esa es una situación en la que setjmp / longjmp tiene sentido. Esas situaciones son similares a situaciones en las que la excepción en otros idiomas (C ++, Java) tiene sentido.
Coroutines
Además del manejo de errores, puedo pensar también en otra situación en la que necesita setjmp / longjmp en C:
Es el caso cuando necesita implementar corrutinas .
Aquí hay un pequeño ejemplo de demostración. Espero que satisfaga la solicitud de Sivaprasad Palas de un código de ejemplo y responda a la pregunta de TheBlastOne de cómo setjmp / longjmp admite la implementación de corroutines (por lo que veo, no se basa en ningún comportamiento nuevo o no estándar).
EDIT:
Podría ser que en realidad es un comportamiento indefinido para hacer un
longjmp
abajo la pila de llamadas (ver comentario de MikeMB, aunque todavía no he tenido oportunidad de verificar que).La siguiente figura muestra el flujo de ejecución:
Nota de advertencia
Cuando utilice setjmp / longjmp, tenga en cuenta que tienen un efecto sobre la validez de las variables locales que a menudo no se consideran.
Cf. mi pregunta sobre este tema .
fuente
setjmp
anteslongjmp
. Esto no es estándar.routineA
yroutineB
usa la misma pila, solo funciona para corrutinas muy primitivas. SiroutineA
llama a anidado profundamenteroutineC
después de la primera llamada aroutineB
y este seroutineC
ejecutaroutineB
como una rutina, entoncesroutineB
podría incluso destruir la pila de retorno (no solo las variables locales) deroutineC
. Entonces, sin asignar una pila exclusiva (¿alloca()
después de llamarrountineB
?), Tendrá serios problemas con este ejemplo si se usa como receta.La teoría es que puede usarlos para el manejo de errores de modo que pueda saltar de una cadena de llamadas profundamente anidada sin necesidad de lidiar con errores de manejo en cada función de la cadena.
Como toda teoría inteligente, esto se desmorona cuando se encuentra con la realidad. Sus funciones intermedias asignarán memoria, tomarán bloqueos, abrirán archivos y harán todo tipo de cosas diferentes que requieran limpieza. Por lo tanto, en la práctica,
setjmp
/longjmp
suelen ser una mala idea, excepto en circunstancias muy limitadas en las que tiene un control total sobre su entorno (algunas plataformas integradas).En mi experiencia, en la mayoría de los casos, siempre que piense que usar
setjmp
/longjmp
funcionaría, su programa es lo suficientemente claro y simple como para que cada llamada de función intermedia en la cadena de llamadas pueda manejar errores, o es tan complicado e imposible de arreglar que debería hacerloexit
cuando encontrar el error.fuente
libjpeg
. Como en C ++, la mayoría de las colecciones de rutinas de C necesitan unstruct *
para operar en algo como colectivo. En lugar de almacenar las asignaciones de memoria de sus funciones intermedias como locales, se pueden almacenar en la estructura. Esto permite que unlongjmp()
manejador libere la memoria. Además, esto no tiene tantas tablas de excepciones malditas que todos los compiladores de C ++ todavía generan 20 años después del hecho.Like every clever theory this falls apart when meeting reality.
De hecho, la asignación temporal y cosas por el estilo sonlongjmp()
complicadas, ya que luego tienes que hacerlosetjmp()
varias veces en la pila de llamadas (una vez para cada función que necesita realizar algún tipo de limpieza antes de salir, que luego necesita "volver a generar la excepción" segúnlongjmp()
el contexto que había recibido inicialmente). Se pone aún peor si esos recursos se modifican después delsetjmp()
, ya que debe declararlosvolatile
para evitar que loslongjmp()
golpee.La combinación de
setjmp
ylongjmp
es "superfuerzagoto
". Utilizar con EXTREMO cuidado. Sin embargo, como han explicado otros, alongjmp
es muy útil para salir de una situación de error desagradable, cuando lo deseaget me back to the beginning
, rápidamente, en lugar de tener que enviar un mensaje de error para 18 capas de funciones.Sin embargo, al igual que
goto
, pero peor, tienes que ser MUY cuidadoso con la forma en que usas esto. Alongjmp
solo lo llevará de regreso al principio del código. No afectará a todos los demás estados que pueden haber cambiado entresetjmp
y volver al punto desetjmp
partida. Por lo tanto, las asignaciones, bloqueos, estructuras de datos medio inicializadas, etc., todavía están asignados, bloqueados y medio inicializados cuando regresa al lugar dondesetjmp
se llamó. Esto significa que tienes que preocuparte mucho por los lugares donde haces esto, que está REALMENTE bien llamarlongjmp
sin causar MÁS problemas. Por supuesto, si lo siguiente que haces es "reiniciar" [después de almacenar un mensaje sobre el error, tal vez], en un sistema integrado en el que has descubierto que el hardware está en mal estado, por ejemplo, entonces está bien.También he visto
setjmp
/longjmp
utilizado para proporcionar mecanismos de subprocesamiento muy básicos. Pero ese es un caso bastante especial, y definitivamente no es cómo funcionan los subprocesos "estándar".Editar: Por supuesto, se podría agregar código para "lidiar con la limpieza", de la misma manera que C ++ almacena los puntos de excepción en el código compilado y luego sabe qué dio una excepción y qué necesita limpieza. Esto implicaría algún tipo de tabla de punteros de función y almacenar "si saltamos desde aquí, llame a esta función, con este argumento". Algo como esto:
Con este sistema, podría realizar un "manejo completo de excepciones como C ++". Pero es bastante complicado y depende de que el código esté bien escrito.
fuente
setjmp
para proteger cada inicialización, a la C ++ ... y vale la pena mencionar que usarlo para subprocesos no es estándar.Dado que menciona incrustado, creo que vale la pena señalar un caso de no uso : cuando su estándar de codificación lo prohíbe. Por ejemplo, MISRA (MISRA-C: 2004: Regla 20.7) y JFS (AV Regla 20): "La macro setjmp y la función longjmp no se utilizarán".
fuente
setjmp
ylongjmp
puede ser muy útil en pruebas unitarias.Supongamos que queremos probar el siguiente módulo:
Normalmente, si la función para probar llama a otra función, puede declarar una función auxiliar para que la llame que imitará lo que hace la función real para probar ciertos flujos. En este caso, sin embargo, la función llama a
exit
que no regresa. El stub necesita emular de alguna manera este comportamiento.setjmp
ylongjmp
puedo hacer eso por ti.Para probar esta función, podemos crear el siguiente programa de prueba:
En este ejemplo, usa
setjmp
antes de ingresar a la función para probar, luego en el stubbedexit
, llamalongjmp
para regresar directamente a su caso de prueba.También tenga en cuenta que el redefined
exit
tiene una variable especial que verifica para ver si realmente desea salir del programa y llama_exit
para hacerlo. Si no lo hace, es posible que su programa de prueba no se cierre correctamente.fuente
He escrito un mecanismo similar a Java manejo de excepciones en C usando
setjmp()
,longjmp()
y las funciones del sistema. Detecta excepciones personalizadas pero también señales comoSIGSEGV
. Cuenta con anidación infinita de bloques de manejo de excepciones, que funciona en llamadas a funciones y admite las dos implementaciones de subprocesos más comunes. Le permite definir una jerarquía de árbol de clases de excepción que cuentan con herencia de tiempo de enlace, y lacatch
declaración recorre este árbol para ver si necesita atrapar o transmitir.Aquí hay una muestra de cómo se ve el código usando esto:
Y aquí hay parte del archivo de inclusión que contiene mucha lógica:
También hay un módulo C que contiene la lógica para el manejo de señales y algo de contabilidad.
Fue extremadamente complicado de implementar, les puedo decir y casi lo dejo. Realmente presioné para hacerlo lo más cerca posible de Java; Me sorprendió lo lejos que llegué solo con C.
Dame un grito si estás interesado.
fuente
main()
saldrá sin ser detectado. Vota esta respuesta :-)Progagation
sección en el README . Publiqué mi código de abril de 1999 en GitHub (vea el enlace en la respuesta editada). Echar un vistazo; era un hueso duro de roer. Sería bueno escuchar lo que piensas.Sin lugar a dudas, el uso más importante de setjmp / longjmp es que actúa como un "salto goto no local". El comando Goto (y hay casos raros en los que necesitará usar goto over para y while bucles) es el más utilizado y seguro en el mismo ámbito. Si usa goto para saltar a través de ámbitos (o asignación automática), lo más probable es que corrompa la pila de su programa. setjmp / longjmp evita esto al guardar la información de la pila en la ubicación a la que desea saltar. Luego, cuando saltas, carga esta información de pila. Sin esta característica, los programadores de C probablemente tendrían que recurrir a la programación en ensamblador para resolver problemas que solo setjmp / longjmp podría resolver. Gracias a Dios existe. Todo en la biblioteca de C es extremadamente importante. Sabrá cuando lo necesite.
fuente