¿Puedo ejecutar free () o close () en un manejador de señales? [duplicar]

Dec 10 2020

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+Cinterrupción para ejecutar la signal_handlerfunción una vez en lugar de matar el proceso de inmediato. Leí en alguna parte que algunas funciones como podrían ser freeno son asincrónicas seguras y NO se ejecutarían en el, signal_handlerpero no estoy seguro de eso.

¿Puedo ejecutar funciones como free, close, exito incluso pthread_joinen un manejador de la señal?

Respuestas

1 dratenik Dec 10 2020 at 15:46

No. Sólo las funciones enumeradas en man 7 signal-safety son seguras para llamar dentro de un manejador de señales.

closeaparece en la lista y debe ser seguro. freeno es. Por razones por las que tendría que mirar su código fuente (contiene bloqueos). exitno es seguro porque puede llamar a controladores de limpieza arbitrarios. Tienes _exitque sale abruptamente sin la limpieza.

1 MarcoBonelli Dec 10 2020 at 15:46

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.

1 JohnBode Dec 10 2020 at 16:30

La respuesta corta es no:

7.1.4 Uso de las funciones de la biblioteca
...
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
C 2011 Borrador en línea

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 stderrse 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.

BasileStarynkevitch Dec 10 2020 at 16:24

¿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) .