SIGNALFD(2) | Linux Programmer's Manual | 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() の振舞いを変更することができる。
バージョン 2.6.26 以前の Linux では、 flags 引き数は未使用であり、0 を指定しなければならない。
signalfd() が返すファイルディスクリプターは以下の操作をサポートしている。
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) が行われると、子プロセスは signalfd ファイルディスクリプターのコピーを 継承する。 子プロセスでこのファイルディスクリプターから read(2) を行うと、子プロセスに対するキューに入っているシグナルに関する 情報が返される。
他のファイルディスクリプターと全く同様に、 signalfd ファイルディスクリプターも execve(2) の前後でオープンされたままとなる。但し、そのファイルディスクリプターに close-on-exec のマーク (fcntl(2) 参照) が付いている場合はクローズされる。 execve(2) の前に読み出し可能となっていた全てのシグナルは新しく起動されたプログラム でも引き続き読み出し可能である (これは伝統的なシグナルの扱いと同じであり、 処理待ちのブロックされたシグナルは execve(2) の前後で処理待ちのままとなる)。
マルチスレッドプログラムにおける signalfd ファイルディスクリプターの扱いは シグナルの標準的な扱いと全く同じである。 言い換えると、あるスレッドが signalfd ファイルディスクリプターから 読み出しを行うと、そのスレッド自身宛てのシグナルとプロセス (すなわち スレッドグループ全体) 宛てのシグナルが読み出される。 (スレッドは同じプロセスの他のスレッド宛てのシグナルを読み出すことはできない。)
成功すると、 signalfd() は signalfd ファイルディスクリプターを返す。 返されるファイルディスクリプターは、 fd が -1 の場合は新規のファイルディスクリプターであり、 fd が有効な signalfd ファイルディスクリプターだった場合は fd 自身である。 エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。
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 に含むファイルディスクリプターのうちいずれか一つから読み出すことができる。
実際の 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/ に書かれている。
2014-07-08 | Linux |