Tengo una aplicación bastante grande bajo cuidado. Como parte de su trabajo, genera algunos procesos secundarios y necesita monitorear su estado (en ejecución, bloqueado).
Las muertes de procesos secundarios se detectaron configurando el controlador de señales para SIGCHLD
usar signal(2)
. Hace un tiempo lo migré a signalfd(2)
. Lo que hice fue simple:
- Se eliminó el controlador de señal para
SIGCHLD
- bloqueado
SIGCHLD
y creado unsignalfd(2)
para capturarSIGCHLD
Mi problema es que el descriptor de archivo que creé no parece capturar SIGCHLD
. Sin embargo, si ignoro el valor de retorno de la read(2)
llamada en ese descriptor y waitpid(-1, &status, WNOHANG)
llamopoderobtener la información sobre los procesos secundarios salidos. Parece que la notificación se entregó pero mi signalfd(2)
descriptor simplemente la ignora.
Me aseguré de tener exactamente un lugar en el programa donde read(2)
se llama el signalfd(2)
descriptor, exactamente un lugar donde waitpid(2)
se llama y exactamente un lugar donde se configura el manejo de la señal.
El código de configuración se ve así:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, nullptr);
int signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (signal_fd == -1) {
/* log failure and exit */
} else {
/* log success */
}
El código de lectura se ve así:
signalfd_siginfo info;
memset(&info, 0, sizeof(info));
if (read(signal_fd, &info, sizeof(info)) == -1) {
/*
* Log failure and return.
* The file descriptor *always* returns EAGAIN, even in
* presence of dead child processes.
*/
return;
}
if (info.ssi_signo == SIGCHLD) {
int status = 0;
int child = waitpid(-1, &status, WNOHANG);
/*
* Process result of waitpid(2). The call is successful even if
* the read of signalfd above returned an error.
*/
}
¿Qué estoy haciendo mal?
Editar:El problema es que read(2)
falla EAGAIN
incluso si hay procesos secundarios muertos listos para ser waitpid(2)
editados, lo que significa que SIGCHLD
se debe haber entregado uno a mi proceso maestro. Sé que read(2)
puede regresar EAGAIN
para descriptores de archivos sin bloqueo y el código lo explica.
Respuesta1
Al migrar desde el manejo de señales en función de signal(2)
usted, sigaction(2)
cambie signalfd(2)
la forma en que recibe las señales. La antigua manera deja las señales desbloqueadas, la nueva necesita que estén bloqueadas.
Si tiene algunas regiones de código en las que no desea que las señales le molesten, debe bloquearlas:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGFOO);
pthread_sigmask(SIG_BLOCK, &mask, nullptr);
{
/* not-to-be-disturbed code here */
}
Esto requiere que más tardedesatascarellos, porque de lo contrario signal(2)
o sigaction(2)
no podrá recogerlos.
{
/* not-to-be-disturbed code here */
}
pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
Sin embargo, signalfd(2)
las señales deben permanecer bloqueadas. Si tiene una ruta de código descuidada durante mucho tiempo que rara vez mira y sigue la forma anterior, es decir, bloquea y desbloquea algunas señales, puede destruir el descriptor del archivo de lectura de señales que obtuvo de signalfd(2)
.
TL;DRRevise su código para detectar cualquier llamada a signal(2)
, sigaction(2)
, pthread_sigmask(2)
etc. al migrar a signalfd(2)
para verificar si no se olvidó de alguna ruta de código que interfiere con la máscara de señal.
(Dos años y medio después puede que sea un poco tarde, pero tal vez la respuesta ayude a alguien).
Respuesta2
Tus read(2)
devoluciones EAGAIN
porque abriste el archivo en modo sin bloqueo con signalfd(..., SFD_NONBLOCK | ...)
( SFD_NONBLOCK
es lo mismo que O_NONBLOCK
).
No abra ni configure los descriptores de archivos en modo sin bloqueo si desea realizar lecturas de bloqueo en ellos.