SIGNALFD(2)

名前
signalfd - シグナル受け付け用のファイルディスクリプターを生成する

書式
#include <sys/signalfd.h>

int signalfd(int fd, const sigset_t *mask, int flags);

説明
signalfd() は、呼び出し元宛てのシグナルを受け付けるために使用されるファイル ディスクリプターを生成する。 この方法はシグナルハンドラーや sigwaitinfo(2) を用いる方法の代わりとなるものであり、このファイルディスクリプターを select(2), poll(2), epoll(7) で監視できるという利点がある。 mask 引き数には、呼び出し元がこのファイルディスクリプター経由で受け付けたい シグナル集合を指定する。この引き数で指定するシグナル集合の内容は、 sigsetops(3) で説明されているマクロを使って初期化することができる。 通常、ファイルディスクリプター経由で受信するシグナル集合は、 そのシグナルがデフォルトの配送方法に基いて処理されるのを防ぐために、 sigprocmask(2) を使ってブロックしておくべきである。 シグナル SIGKILL と SIGSTOP を signalfd ファイルディスクリプター経由で受信することはできない。 これらのシグナルが mask で指定された場合には黙って無視される。 fd 引き数が -1 の場合、 signalfd() は新しいファイルディスクリプターを生成し、 mask で指定されたシグナル集合をそのファイルディスクリプターに関連付ける。 fd 引き数が -1 以外の場合、 fd には有効な既存の signalfd ファイルディスクリプターを指定しなければならず、 そのディスクリプターに関連付けられているシグナル集合は mask を使って置き換えられる。 Linux 2.6.27 以降では、 以下の値のいくつかをビット単位の論理和 (OR) で指定することで、 signalfd() の振舞いを変更することができる。

SFD_NONBLOCK
    新しく生成されるオープンファイル記述 (open file description) の O_NONBLOCK ファイルステータスフラグをセットする。 このフラグを使うことで、 O_NONBLOCK をセットするために fcntl(2) を追加で呼び出す必要がなくなる。

SFD_CLOEXEC
    新しいファイルディスクリプターに対して close-on-exec (FD_CLOEXEC) フラグをセットする。 このフラグが役に立つ理由については、 open(2) の O_CLOEXEC フラグの説明を参照のこと。

バージョン 2.6.26 以前の Linux では、 flags 引き数は未使用であり、0 を指定しなければならない。 signalfd() が返すファイルディスクリプターは以下の操作をサポートしている。

read(2)
    mask に指定されているシグナルのうち一つ以上がそのプロセスに対して 処理待ち (pending) であれば、それらのシグナルの情報が read(2) に渡されたバッファーを使って、 signalfd_siginfo 構造体に格納されて返される。 read(2) は、バッファーに格納可能な範囲でできるだけ多くの処理待ちのシグナルに ついての情報を返す。 バッファーは最低でも sizeof(struct signalfd_siginfo) バイトの大きさがなければならない。 read(2) の返り値は読み出されたトータルのバイト数である。

    read(2) が行われた結果、シグナルは消費され、 これらのシグナルはそのプロセスに対しては処理待ちではなくなる (つまり、シグナルハンドラーで捕捉されることもなく、 sigwaitinfo(2) を使って受け取ることもできなくなる)。

    mask に指定されているシグナルがそのプロセスに対して一つも処理待ちでなければ、 read(2) は、 mask で指定されたシグナルのうちいずれか一つがそのプロセスに対して発生するまで 停止 (block) する、もしくはファイルディスクリプターが非停止 (nonblocking) に設定されている場合はエラー EAGAIN で失敗する。

poll(2), select(2) (と同様の操作)
    mask に指定されたシグナルのうち一つ以上がそのプロセスに対して処理待ちであれば、 ファイルディスクリプターは読み出し可能となる (select(2) の readfds 引き数や poll(2) の POLLIN フラグ)。

    signalfd ファイルディスクリプターは、これ以外のファイルディスクリプター 多重 API である pselect(2), ppoll(2), epoll(7) もサポートしている。

close(2)
    ファイルディスクリプターがそれ以降は必要なくなった際には、クローズすべきである。 同じ signalfd オブジェクトに関連付けられたファイルディスクリプターが全て クローズされると、そのオブジェクト用の資源がカーネルにより解放される。

  signalfd_siginfo 構造体
signalfd ファイルディスクリプターからの read(2) で返される signalfd_siginfo 構造体のフォーマットは以下の通りである。
struct signalfd_siginfo {
struct signalfd_siginfo {
    uint32_t ssi_signo;   /* シグナル番号 */
    int32_t  ssi_errno;   /* エラー番号 (未使用) */
    int32_t  ssi_code;    /* シグナルコード */
    uint32_t ssi_pid;     /* 送信元の PID */
    uint32_t ssi_uid;     /* 送信元の実 UID */
    int32_t  ssi_fd;      /* ファイルディスクリプター (SIGIO) */
    uint32_t ssi_tid;     /* カーネルタイマー ID (POSIX タイマー)
    uint32_t ssi_band;    /* Band イベント (SIGIO) */
    uint32_t ssi_overrun; /* POSIX タイマーのオーバーラン回数 */
    uint32_t ssi_trapno;  /* シグナルの原因となったトラップ番号 */
    int32_t  ssi_status;  /* 終了ステータスかシグナル (SIGCHLD) */
    int32_t  ssi_int;     /* sigqueue(3) から送られた整数 */
    uint64_t ssi_ptr;     /* sigqueue(3) から送られたポインター */
    uint64_t ssi_utime;   /* 消費したユーザー CPU 時間 (SIGCHLD) */
    uint64_t ssi_stime;   /* 消費したシステム CPU 時間 (SIGCHLD) */
    uint64_t ssi_addr;    /* シグナルを生成したアドレス
                             (ハードウェアが生成したシグナルの場合) */
    uint8_t  pad[X];      /* pad の大きさは 128 バイト
                             (将来のフィールド追加用の場所の確保) */
};
signalfd_siginfo 構造体の各フィールドは、 siginfo_t 構造体の同じような名前のフィールドと同様である。 siginfo_t 構造体については sigaction(2) に説明がある。 返された signalfd_siginfo 構造体の全てのフィールドがあるシグナルに対して有効なわけではない。 どのフィールドが有効かは、 ssi_code フィールドで返される値から判定することができる。 このフィールドは siginfo_t の si_code フィールドと同様である。詳細は sigaction(2) を参照。

  fork(2) での扱い
fork(2) が行われると、子プロセスは signalfd ファイルディスクリプターのコピーを 継承する。 子プロセスでこのファイルディスクリプターから read(2) を行うと、子プロセスに対するキューに入っているシグナルに関する 情報が返される。

  execve(2) での扱い
他のファイルディスクリプターと全く同様に、 signalfd ファイルディスクリプターも execve(2) の前後でオープンされたままとなる。但し、そのファイルディスクリプターに close-on-exec のマーク (fcntl(2) 参照) が付いている場合はクローズされる。 execve(2) の前に読み出し可能となっていた全てのシグナルは新しく起動されたプログラム でも引き続き読み出し可能である (これは伝統的なシグナルの扱いと同じであり、 処理待ちのブロックされたシグナルは execve(2) の前後で処理待ちのままとなる)。

  スレッドでの扱い
マルチスレッドプログラムにおける signalfd ファイルディスクリプターの扱いは シグナルの標準的な扱いと全く同じである。 言い換えると、あるスレッドが signalfd ファイルディスクリプターから 読み出しを行うと、そのスレッド自身宛てのシグナルとプロセス (すなわち スレッドグループ全体) 宛てのシグナルが読み出される。 (スレッドは同じプロセスの他のスレッド宛てのシグナルを読み出すことはできない。)

返り値
成功すると、 signalfd() は signalfd ファイルディスクリプターを返す。 返されるファイルディスクリプターは、 fd が -1 の場合は新規のファイルディスクリプターであり、 fd が有効な signalfd ファイルディスクリプターだった場合は fd 自身である。 エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。

エラー

EBADF
    ファイルディスクリプター fd が有効なファイルディスクリプターでない。

EINVAL
    fd が有効な signalfd ファイルディスクリプターではない。

EINVAL
    flags が無効である。もしくは、Linux 2.6.26 以前の場合には flags が 0 以外である。

EMFILE
    オープン済みのファイルディスクリプターの数がプロセスあたりの上限に 達していた。

ENFILE
    オープン済みのファイル総数がシステム全体の上限に達していた。

ENODEV
    (カーネル内の) 無名 inode デバイスをマウントできなかった。

ENOMEM
    新しい signalfd ファイルディスクリプターを生成するのに十分なメモリーがなかった。

バージョン
signalfd() はカーネル 2.6.22 以降の Linux で利用可能である。 正しく動作する glibc 側のサポートはバージョン 2.8 以降で提供されている。 signalfd4() システムコール (「注意」参照) は カーネル 2.6.27 以降の Linux で利用可能である。

準拠
signalfd() と signalfd4() は Linux 固有である。

注意
一つのプロセスは複数の signalfd ファイルディスクリプターを生成することができる。 これにより、異なるファイルディスクリプターで異なるシグナルを受け取ることが できる (この機能は select(2), poll(2), epoll(7) を使ってファイルディスクリプターを監視する場合に有用かもしれない。 異なるシグナルが到着すると、異なるファイルディスクリプターが利用可能に なるからだ)。 一つのシグナルが二つ以上のファイルディスクリプターの mask に含まれている場合、そのシグナルの発生はそのシグナルを mask に含むファイルディスクリプターのうちいずれか一つから読み出すことができる。

  C ライブラリとカーネル ABI の違い
実際の Linux のシステムコールでは size_t sizemask という引き数が追加で必要である。この引き数で mask のサイズを指定する。 glibc の signalfd() ラッパー関数にはこの引き数は含まれず、 ラッパー関数が必要な値を計算して内部で呼び出すシステムコールに提供する。 下層にある Linux システムコールは二種類あり、 signalfd() と、もっと新しい signalfd4() である。 signalfd() は flags 引き数を実装していない。 signalfd4() では上記の値の flags が実装されている。 glibc 2.9 以降では、 signalfd() のラッパー関数は、 signalfd4() が利用可能であれば、これを使用する。

バグ
カーネル 2.6.25 より前では、 sigqueue(3) により送信されたシグナルと一緒に渡されるデータでは、フィールド ssi_ptr と ssi_int は設定されない。

例
下記のプログラムは、シグナル SIGINT と SIGQUIT を signalfd ファイルディスクリプター経由で受信する。 シグナル SIGQUIT 受信後にプログラムは終了する。 以下に示すシェルセッションにこのプログラムの使い方を示す。
$ ./signalfd_demo
^C                   # Control-C generates SIGINT
Got SIGINT
^C
Got SIGINT
^\                    # Control-\ generates SIGQUIT
Got SIGQUIT
$

  プログラムのソース
#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
    sigset_t mask;
    int sfd;
    struct signalfd_siginfo fdsi;
    ssize_t s;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGQUIT);
    /* Block signals so that they aren't handled
       according to their default dispositions */
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
        handle_error("sigprocmask");
    sfd = signalfd(-1, &mask, 0);
    if (sfd == -1)
        handle_error("signalfd");
    for (;;) {
        s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
        if (s != sizeof(struct signalfd_siginfo))
            handle_error("read");
        if (fdsi.ssi_signo == SIGINT) {
            printf("Got SIGINT\n");
        } else if (fdsi.ssi_signo == SIGQUIT) {
            printf("Got SIGQUIT\n");
            exit(EXIT_SUCCESS);
        } else {
            printf("Read unexpected signal\n");
        }
    }
}

関連項目
eventfd(2), poll(2), read(2), select(2), sigaction(2), sigprocmask(2), sigwaitinfo(2), timerfd_create(2), sigsetops(3), sigwait(3), epoll(7), signal(7)

この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部である。 プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。