Файловый дескриптор из `signalfd(2)` никогда не готов к чтению

Файловый дескриптор из `signalfd(2)` никогда не готов к чтению

У меня есть довольно большое приложение под присмотром. В рамках своей работы оно порождает несколько дочерних процессов и должно отслеживать их состояние (работает, упал).

Смерти дочерних процессов были обнаружены путем установки обработчика сигналов для SIGCHLDиспользования signal(2). Некоторое время назад я перенес его на signalfd(2). То, что я сделал, было простым:

  1. удалил обработчик сигнала дляSIGCHLD
  2. заблокировал SIGCHLDи создал signalfd(2)для захватаSIGCHLD

Моя проблема в том, что созданный мной дескриптор файла, похоже, не захватывает SIGCHLD. Однако, если я проигнорирую возвращаемое значение read(2)вызова этого дескриптора и вызову waitpid(-1, &status, WNOHANG)Iможетполучить информацию о завершенных дочерних процессах. Так что похоже, что уведомление доставлено, но мой signalfd(2)дескриптор просто игнорирует его.

Я позаботился о том, чтобы в программе было ровно одно место, где read(2)вызывается дескриптор signalfd(2), ровно одно место, где waitpid(2)вызывается , и ровно одно место, где настраивается обработка сигнала.

Код установки выглядит так:

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

Код чтения выглядит так:

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

Что я делаю не так?

Редактировать:Проблема в том, что это read(2)не удается, EAGAINдаже если есть мертвые дочерние процессы, готовые к waitpid(2)-ed, что означает, что a SIGCHLDдолжно быть доставлено моему главному процессу. Я знаю, что это read(2)может вернуться EAGAINдля неблокирующих файловых дескрипторов, и код учитывает это.

решение1

При переходе с обработки сигналов на основе signal(2)или sigaction(2)на signalfd(2)вы меняете способ получения сигналов. Старый способ оставляет сигналы незаблокированными, новый требует их блокировки.

Если у вас есть области кода, в которых вы не хотите, чтобы вас беспокоили сигналы, вам необходимо заблокировать их:

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

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

Это требует, чтобы вы позжеразблокироватьих, потому что в противном случае signal(2)или sigaction(2)не будет возможности их забрать.

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

pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);

Однако, для signalfd(2)сигналов необходимо оставаться заблокированными. Если у вас есть длинный заброшенный путь кода, на который вы редко заглядываете, и он следует старому пути, т. е. блокирует и разблокирует некоторые сигналы, он может испортить ваш дескриптор файла чтения сигналов, который вы получили из signalfd(2).

TL;DRПроверьте свой код на наличие вызовов signal(2), sigaction(2)и pthread_sigmask(2)т. д. при переходе на , signalfd(2)чтобы убедиться, что вы не забыли о каком-либо кодовом пути, который нарушает маску сигнала.

(Спустя два с половиной года может быть немного поздно, но, возможно, ответ кому-то поможет.)

решение2

Ваш read(2)возврат EAGAINсвязан с тем, что вы открыли файл в неблокируемом режиме с помощью signalfd(..., SFD_NONBLOCK | ...)( SFD_NONBLOCKто же самое, что и O_NONBLOCK).

Не открывайте и не переводите файловые дескрипторы в неблокируемый режим, если вы хотите выполнять блокирующие чтения для них.

Связанный контент