O descritor de arquivo de `signalfd(2)` nunca está pronto para leitura

O descritor de arquivo de `signalfd(2)` nunca está pronto para leitura

Eu tenho um aplicativo bastante grande sob cuidados. Como parte de seu trabalho, ele gera alguns processos filhos e precisa monitorar seu estado (em execução, travado).

As mortes de processos infantis foram detectadas configurando o manipulador de sinal para SIGCHLDuso signal(2). Há algum tempo migrei para signalfd(2). O que fiz foi simples:

  1. removeu o manipulador de sinal paraSIGCHLD
  2. bloqueei SIGCHLDe criei um signalfd(2)para capturarSIGCHLD

Meu problema é que o descritor de arquivo que criei parece não capturar arquivos SIGCHLD. No entanto, se eu ignorar o valor de retorno da read(2)chamada nesse descritor e ligar waitpid(-1, &status, WNOHANG),podeobter as informações sobre processos filhos encerrados. Parece que a notificação foi entregue, mas meu signalfd(2)descritor simplesmente a ignora.

Certifiquei-me de ter exatamente um local no programa onde read(2)é chamado no signalfd(2)descritor, exatamente um local onde waitpid(2)é chamado e exatamente um local onde o tratamento do sinal é configurado.

O código de configuração é assim:

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 */
}

O código de leitura fica assim:

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.
     */
}

O que estou fazendo de errado?

Editar:O problema é que read(2)falha EAGAINmesmo que haja processos filhos mortos prontos para serem waitpid(2)editados, o que significa que um SIGCHLDdeve ter sido entregue ao meu processo mestre. Eu sei que isso read(2)pode retornar EAGAINpara descritores de arquivo sem bloqueio e o código é responsável por isso.

Responder1

Ao migrar do tratamento de sinais com base em signal(2)ou sigaction(2)para signalfd(2)você altera a forma como recebe os sinais. A forma antiga deixa os sinais desbloqueados, a nova precisa deles bloqueados.

Se você tiver algumas regiões de código nas quais não deseja ser perturbado por sinais, será necessário bloqueá-las:

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGFOO);
pthread_sigmask(SIG_BLOCK, &mask, nullptr);

{
    /* not-to-be-disturbed code here */
}

Isso requer que você mais tardedesbloqueareles, porque caso contrário signal(2)ou sigaction(2)não será capaz de pegá-los.

{
    /* not-to-be-disturbed code here */
}

pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);

Porém, pois signalfd(2)os sinais devem permanecer bloqueados. Se você tem um caminho de código há muito negligenciado que raramente olha e segue o caminho antigo, ou seja, bloqueia e desbloqueia alguns sinais, ele pode destruir seu descritor de arquivo de leitura de sinal que você obteve signalfd(2).

DRVerifique se há chamadas para signal(2), sigaction(2), pthread_sigmask(2)etc. em seu código ao migrar para signalfd(2)para verificar se você não esqueceu algum caminho de código que mexe com a máscara de sinal.

(Dois anos e meio depois pode ser um pouco tarde, mas talvez a resposta ajude alguém.)

Responder2

Seu read(2)retorno EAGAINporque você abriu o arquivo em modo sem bloqueio com signalfd(..., SFD_NONBLOCK | ...)( SFD_NONBLOCKé o mesmo que O_NONBLOCK).

Não abra ou defina descritores de arquivo para o modo sem bloqueio se quiser fazer leituras de bloqueio neles.

informação relacionada