El siguiente ejemplo de The Programming Linux Programming Interface de Michael Kerrisk
static void sigHandler(int sig){ printf("Ouch!\n"); } int main(int argc, char *argv[]) { int j; if (signal(SIGINT, sigHandler) == SIG_ERR) errExit("signal"); for (j = 0; ; j++){ printf("%d\n", j); sleep(3); } }se supone que imprime "¡Ay!" al terminal cada vez que el usuario pulsa Control-C (CTRL+C); en el propio ejemplo del autor, lo escribe dos veces antes de salir finalmente de la terminal con Control-\ (CTRL+\ ).
Cuando hago esto, el programa funciona como se esperaba solo en la primera ejecución de CTRL+C. Si lo escribo por segunda vez, como lo hace el autor en su ejemplo, mi programa cierra la terminal, no imprime "¡Ay!" ni continúa ejecutándose (en bucle).
Utilicé exactamente el mismo código que se proporciona aquí, en el sitio web del libro:
Por lo general, la signal necesita que se vuelva a instalar el controlador de señal. De lo contrario, por defecto es SIG_DFL (acción por defecto correspondiente a la señal). La acción predeterminada para SIGINT es terminar el programa.
Tenga en cuenta que printf(3) no es una de las funciones asíncronas seguras. Entonces podría escribir (2) para hacer lo mismo. Consulte la lista POSIX de funciones seguras para señales asíncronas.
Reinstalarlo debería funcionar como se esperaba:
static void sigHandler(int sig){ signal(SIGINT, sigHandler); write(STDOUT_FILENO, "Ouch!\n", 6); } Esta es una de las razones por las que debe evitar signal y usar sigaction en su lugar. El comportamiento mencionado anteriormente no es universal en todas las plataformas. Entonces, tal vez, la plataforma que está ejecutando no es en la que el autor probó su código o está usando un kernel de Linux diferente.
El comportamiento en el que necesita volver a instalar el controlador de señal al recibir una señal es el comportamiento del Sistema V. Pero la semántica BSD no necesita reinstalación. Hasta hace poco, Linux mostraba el comportamiento de System V, pero parece haberse solucionado en el kernel reciente y no veo esto en mi kernel 3.19, pero puedo ver el kernel 2.6.32 (considerablemente antiguo).
La documentación de Signal dice:
The situation on Linux is as follows: * The kernel's signal() system call provides System V semantics. * By default, in glibc 2 and later, the signal() wrapper function does not invoke the kernel system call. Instead, it calls sigaction(2) using flags that supply BSD semantics. This default behavior is provided as long as the _BSD_SOURCE feature test macro is defined. By default, _BSD_SOURCE is defined; it is also implicitly defined if one defines _GNU_SOURCE, and can of course be explicitly defined. * On glibc 2 and later, if the _BSD_SOURCE feature test macro is not defined, then signal() provides System V semantics. (The default implicit definition of _BSD_SOURCE is not provided if one invokes gcc(1) in one of its standard modes (-std=xxx or -ansi) or defines various other feature test macros such as _POSIX_SOURCE, _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).)
Por lo tanto, podría trabajar definiendo _BSD_SOURCE para obtener la semántica BSD. Por lo tanto, el comportamiento que observa es más probable porque la signal en su sistema sigue la semántica de System V y Linux reciente (y probablemente en lo que Kerrisk lo probó) sigue la semántica de BSD.
no debe usar printf() en los controladores de señales y excepciones porque no son reentrantes. printf también almacena datos en la memoria antes de poner esto en la consola, por lo que usar fflush() ayudará a imprimir, pero no se recomienda. para fines de prueba, use el contador (bandera) en el controlador y use el controlador externo printf. no use la señal () para registrar el controlador ya que cada versión de Unix (BSD, Linux) no proporciona la misma implementación. En su lugar, utilice sigaction.