`signalfd(2)` からのファイル記述子は読み取り準備が完了していません

`signalfd(2)` からのファイル記述子は読み取り準備が完了していません

かなり大きなアプリケーションを管理しています。その仕事の一環として、いくつかの子プロセスを生成し、それらの状態 (実行中、クラッシュ) を監視する必要があります。

SIGCHLD子プロセスの終了は、を使用してシグナル ハンドラーを設定することによって検出されましたsignal(2)。しばらく前に、これを に移行しましたsignalfd(2)。私が行ったことは単純でした。

  1. シグナルハンドラを削除しましたSIGCHLD
  2. ブロックされSIGCHLDsignalfd(2)キャプチャするために作成されたSIGCHLD

私の問題は、私が作成したファイル記述子がキャプチャしていないように見えることです。ただし、その記述子の呼び出しSIGCHLDの戻り値を無視して、 Iを呼び出すと、read(2)waitpid(-1, &status, WNOHANG)できる終了した子プロセスに関する情報を取得します。通知は配信されたように見えますが、記述子はsignalfd(2)それを無視します。

read(2)プログラム内に、記述子で が呼び出される場所が正確に 1 つsignalfd(2)、 が呼び出される場所が正確に 1 つwaitpid(2)、そしてシグナル処理がセットアップされる場所が正確に 1 つあることを確認しました。

セットアップコードは次のようになります。

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

何が間違っているのでしょうか?

編集:問題は、 -edされる準備ができている死んだ子プロセスがある場合でも がread(2)失敗することです。つまり、 がマスター プロセスに配信されている必要があります。 が非ブロッキング ファイル記述子を返す可能性があることはわかっており、コードはそれを考慮しています。EAGAINwaitpid(2)SIGCHLDread(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)

要約signal(2)に移行するときに、sigaction(2)pthread_sigmask(2)などへの呼び出しについてコードを検査し、signalfd(2)シグナル マスクを混乱させるコードパスを忘れていないかどうかを確認します。

(2年半後というのは少し遅いかもしれませんが、その答えが誰かの役に立つかもしれません。)

答え2

(と同じ)を使用して非ブロッキング モードでファイルを開いたため、がread(2)返されます。EAGAINsignalfd(..., SFD_NONBLOCK | ...)SFD_NONBLOCKO_NONBLOCK

ファイル記述子に対してブロッキング読み取りを行う場合は、ファイル記述子を開いたり、非ブロッキング モードに設定したりしないでください。

関連情報