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 SIGCHLD
uso signal(2)
. Há algum tempo migrei para signalfd(2)
. O que fiz foi simples:
- removeu o manipulador de sinal para
SIGCHLD
- bloqueei
SIGCHLD
e criei umsignalfd(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 EAGAIN
mesmo que haja processos filhos mortos prontos para serem waitpid(2)
editados, o que significa que um SIGCHLD
deve ter sido entregue ao meu processo mestre. Eu sei que isso read(2)
pode retornar EAGAIN
para 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 EAGAIN
porque 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.