Der Dateideskriptor von `signalfd(2)` ist nie lesebereit

Der Dateideskriptor von `signalfd(2)` ist nie lesebereit

Ich betreue eine ziemlich große Anwendung. Im Rahmen ihrer Arbeit erzeugt sie einige untergeordnete Prozesse und muss deren Status überwachen (wird ausgeführt, ist abgestürzt).

Abstürze von untergeordneten Prozessen wurden erkannt, indem der Signalhandler für SIGCHLDdie Verwendung eingestellt wurde signal(2). Vor einiger Zeit habe ich ihn auf migriert signalfd(2). Was ich getan habe, war einfach:

  1. entfernte den Signal-Handler fürSIGCHLD
  2. blockiert SIGCHLDund erstellt eine signalfd(2)zu erfassenSIGCHLD

Mein Problem ist, dass der von mir erstellte Dateideskriptor anscheinend nicht erfasst SIGCHLD. Wenn ich jedoch den Rückgabewert von read(2)call auf diesem Deskriptor ignoriere und aufrufe waitpid(-1, &status, WNOHANG),dürfenInformationen zu beendeten untergeordneten Prozessen abrufen. Es sieht also so aus, als ob die Benachrichtigung zugestellt wurde, aber mein signalfd(2)Deskriptor ignoriert sie einfach.

Ich habe darauf geachtet, dass es im Programm genau eine Stelle gibt, an der der Deskriptor read(2)aufgerufen wird , genau eine Stelle, an der aufgerufen wird, und genau eine Stelle, an der die Signalverarbeitung eingerichtet wird.signalfd(2)waitpid(2)

Der Setup-Code sieht folgendermaßen aus:

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

Der Lesecode sieht folgendermaßen aus:

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

Was mache ich falsch?

Bearbeiten:Das Problem ist, dass dies read(2)fehlschlägt, EAGAINauch wenn tote Kindprozesse vorhanden sind, die mit waitpid(2)-ed bearbeitet werden können, was bedeutet, dass ein SIGCHLDan meinen Hauptprozess übermittelt worden sein muss. Ich weiß, dass dies für nicht blockierende Dateideskriptoren read(2)zurückgegeben werden kann EAGAIN, und der Code berücksichtigt dies.

Antwort1

Wenn Sie von der Signalverarbeitung auf Basis von signal(2)oder sigaction(2)zu migrieren signalfd(2), ändern Sie die Art und Weise, wie Sie Signale empfangen. Bei der alten Methode bleiben die Signale nicht blockiert, bei der neuen müssen sie blockiert werden.

Wenn Sie Codebereiche haben, in denen Sie nicht durch Signale gestört werden möchten, müssen Sie diese blockieren:

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

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

Dies erfordert, dass Sie späterentsperrensie, weil signal(2)wir sigaction(2)sie sonst nicht abholen können.

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

pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);

Allerdings signalfd(2)müssen die Signale blockiert bleiben. Wenn Sie einen lange vernachlässigten Codepfad haben, den Sie selten ansehen und der dem alten Weg folgt, d. h. einige Signale blockiert und entsperrt, kann dies Ihren Signallese-Dateideskriptor zerstören, den Sie von erhalten haben signalfd(2).

Kurz zusammengefasstÜberprüfen Sie Ihren Code bei der Migration zu auf Aufrufe von signal(2), sigaction(2), pthread_sigmask(2)usw., signalfd(2)um sicherzustellen, dass Sie keinen Codepfad vergessen haben, der die Signalmaske durcheinanderbringt.

(Zweieinhalb Jahre später ist vielleicht etwas spät, aber vielleicht hilft die Antwort jemandem.)

Antwort2

Ihre read(2)Rückgabewerte sind EAGAIN, da Sie die Datei im nicht blockierenden Modus mit signalfd(..., SFD_NONBLOCK | ...)( geöffnet haben SFD_NONBLOCK, dasselbe wie O_NONBLOCK) angezeigt werden.

Öffnen Sie keine Dateideskriptoren und setzen Sie sie nicht in den nicht blockierenden Modus, wenn Sie blockierende Lesevorgänge auf ihnen ausführen möchten.

verwandte Informationen