Cuando llamo a open("./fifo",O_RDONLY) , la llamada al sistema se bloqueará porque nadie está escribiendo en el fifo ./fifo . Si se recibe una señal durante ese tiempo que no tiene controlador de señal, el proceso finaliza instantáneamente. Hasta aquí todo bien. Pero cuando se recibe una señal que tiene un controlador de señal, el controlador de señal se ejecuta y la llamada al open() aún se bloquea.
¿Cómo puedo hacer que open() regrese cuando capto la señal?
Traté de bloquear la señal, eso no funciona porque no hay un argumento sigmask para open() como lo hay para pselect() . Usar O_NONBLOCK tampoco funciona, porque entonces open() regresará con un error, ya sea que haya una señal o no. Eliminar el controlador de señal tampoco es bueno porque quiero poder reaccionar a la señal.
Mi código de prueba:
#include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdbool.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static volatile bool end=0; static void catchSignal(int signal) { (void)signal; const char *s="Catched Signal\n"; write(STDERR_FILENO,s,strlen(s)); end=1; } static int openFile(void) { int fd=open("./in",O_RDONLY); if(fd<0) { perror("can't open file"); exit(1); } return fd; } int main() { if(SIG_ERR==signal(SIGTERM,catchSignal)) { perror("cant set up signal handler"); return -1; } int fd = openFile(); while(end==0) { puts("Still running"); usleep(300UL*1000); } puts("End now"); if(fd>0) { close(fd); } return 0; }La función signal() es problemática debido a un historial de implementaciones con diferentes detalles. De acuerdo con su página de manual de Linux :
El único uso portátil de
signal()es establecer la disposición de una señal enSIG_DFLoSIG_IGN. La semántica cuando se usasignal()para establecer un controlador de señal varía según los sistemas (y POSIX.1 permite explícitamente esta variación); no lo use para este propósito .
(Énfasis en el original)
En lugar de signal() , deberías usar sigaction() :
struct sigaction sa = { .sa_handler = catchSignal }; if (SIG_ERR == sigaction(SIGTERM, &sa, NULL)) Tenga en cuenta que entre los campos de una struct sigaction se encuentra sa_flags , una máscara de bits con la que puede seleccionar entre los diversos comportamientos implementados históricamente por diferentes versiones de signal() . En particular, si no incluye el indicador SA_RESTART , ya que el anterior no lo hace, entonces no debería ver que las llamadas al sistema se reanudan automáticamente cuando son interrumpidas por una señal (excepto aquellas pocas que están explícitamente especificadas para hacerlo).
Cuando strace su programa, ve que las funciones de señal () establecen el indicador SA_RESTART para la señal:
rt_sigaction(SIGTERM, {sa_handler=0x562f2a8c3249, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fb504d2d210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0lo que significa que la llamada al sistema open() se reiniciará automáticamente después de manejar la señal.
Puede usar sigaction() para tener un control más detallado sobre el manejo de la señal y no configurar SA_RESTART:
struct sigaction sa; memset (&sa, 0, sizeof (sa)); sa.sa_handler = catchSignal; sa.sa_flags = 0; sigemptyset (&sa.sa_mask); if (sigaction (SIGTERM, &sa, NULL) == -1) { perror("sigaction"); return -1; }puede usar O_NONBLOCK en cuyo caso open() regresará inmediatamente, y bloqueará tan pronto como fcntl(2) cancele O_NONBLOCK .
Lea la página de manual, ya que probablemente tenga alguna forma de hacer que open(2) devuelva -1 y errno igual a EINTR . Pero el uso normal es el que se ha descrito, la llamada se vuelve a emitir, por lo que el controlador de la señal no hace que la llamada se interrumpa (mucho del código depende de este comportamiento). No estoy seguro de esto, pero creo que solo pause(2) y select(2) y los amigos se interrumpen (y regresan) cuando se recibe una señal no ignorada.
Vale la pena señalar que solo el hilo que está bloqueado en una llamada interrumpible y recibe la señal se despierta y la llamada se interrumpe, y el hilo que recibe la interrupción puede ser cualquiera de los que haya iniciado en su proceso.