¿Puedo ejecutar free () o close () en un manejador de señales? [duplicar]
Tengo un código que se parece a esto:
//global variables
void signal_handler() {
//deallocation of global variables
free(foo);
close(foo_2);
exit(0);
}
int main () {
signal(SIGINT, signal_handler);
//irrelevant code
}
Como puede ver, cambié la CTRL+C
interrupción para ejecutar la signal_handler
función una vez en lugar de matar el proceso de inmediato. Leí en alguna parte que algunas funciones como podrían ser free
no son asincrónicas seguras y NO se ejecutarían en el, signal_handler
pero no estoy seguro de eso.
¿Puedo ejecutar funciones como free
, close
, exit
o incluso pthread_join
en un manejador de la señal?
Respuestas
No. Sólo las funciones enumeradas en man 7 signal-safety son seguras para llamar dentro de un manejador de señales.
close
aparece en la lista y debe ser seguro. free
no es. Por razones por las que tendría que mirar su código fuente (contiene bloqueos). exit
no es seguro porque puede llamar a controladores de limpieza arbitrarios. Tienes _exit
que sale abruptamente sin la limpieza.
Técnicamente, puede compilar un programa que llame a tales funciones en un manejador de señales, nada le impide hacer eso. Sin embargo, resultará en un comportamiento indefinido si la función que está intentando ejecutar no es segura para señales asíncronas. No es que una función insegura simplemente "NO se ejecute" como usted dice, muy bien podrían hacerlo, pero aún así sería un comportamiento indefinido.
Se documenta una lista de funciones seguras para señales asíncronas en man 7 signal-safety. La close()
función es segura, mientras que free()
y phtread_join()
no lo son. La exit()
función tampoco es segura para llamar desde un manejador de señales, si desea salir de dicho contexto tendrá que hacerlo usando en su _exit()lugar.
La única forma de llamar de forma segura a una función que no es asíncrona-señal-segura al recibir una señal es "recordar" que tienes que llamarla (por ejemplo, estableciendo una variable global) y luego hacerlo después de regresar del manejador de señales.
La respuesta corta es no:
7.1.4 Uso de las funciones de la bibliotecaC 2011 Borrador en línea
...
4 No se garantiza que las funciones de la biblioteca estándar sean reentrantes y pueden modificar objetos con una duración de almacenamiento estática o de subprocesos. 188)
188) Por lo tanto, un manejador de señales no puede, en general, llamar a funciones de biblioteca estándar
Ejemplo del mundo real de las consecuencias: trabajé en un sistema que se comunicaba con una base de datos de Access. Hubo un controlador de señales que intentó escribir un mensaje de error en la consola con fprintf
, pero de alguna manera durante el proceso de manejo de señales stderr
se asignó al archivo .mdb que almacenaba la base de datos, sobrescribiendo el encabezado y arruinando la base de datos sin posibilidad de reparación.
Honestamente, no hay mucho que pueda hacer en un manejador de señales además de establecer una bandera para que se verifique en otro lugar.
¿Puedo ejecutar free () o close () en un manejador de señales?
Definitivamente no deberías. Ver señal (7) y señal-seguridad (7)
En la práctica, podría funcionar como desea quizás más de la mitad del tiempo. IIRC, el compilador de GCC está funcionando como usted quiere, y normalmente funciona.
Un mejor enfoque es usar algo de escritura (2) en una tubería (7) (desde el interior de su manejador de señales) y de vez en cuando verifique esa tubería (en su programa principal) con poll (2) o cosas relacionadas.
O puede establecer algunos volatile sigatomic_t flag;
(quizás debería estarlo también _Atomic) en su manejador de señales, y verificar esa bandera en otro lugar (en el programa principal, fuera de los manejadores de señales).
Qt lo está explicando mejor de lo que podría hacerlo en unos minutos.
En Linux, consulte también signalfd (2) y eventfd (2) .