El descriptor de archivo de `signalfd(2)` nunca está listo para leer

El descriptor de archivo de `signalfd(2)` nunca está listo para leer

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 SIGCHLDusar signal(2). Hace un tiempo lo migré a signalfd(2). Lo que hice fue simple:

  1. Se eliminó el controlador de señal paraSIGCHLD
  2. bloqueado SIGCHLDy creado un signalfd(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 EAGAINincluso si hay procesos secundarios muertos listos para ser waitpid(2)editados, lo que significa que SIGCHLDse debe haber entregado uno a mi proceso maestro. Sé que read(2)puede regresar EAGAINpara 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 EAGAINporque abriste el archivo en modo sin bloqueo con signalfd(..., SFD_NONBLOCK | ...)( SFD_NONBLOCKes 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.

información relacionada