EVENTFD(2) | Linux Programmer's Manual | EVENTFD(2) |
eventfd - イベント通知用のファイルディスクリプターを生成する
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
eventfd() は "eventfd オブジェクト" を生成する。 eventfd オブジェクトはユーザー空間アプリケーションがイベント待ち受け/通知用の 仕組みとして使うことができる。また、カーネルがユーザー空間アプリケーションに イベントを通知するためにも使うことができる。 このオブジェクトには、unsigned の 64 ビット整数 (uint64_t) 型のカウンターが含まれており、このカウンターはカーネルにより管理される。 このカウンターは initval 引き数で指定された値で初期化される。
以下の値のいくつかをビット単位の論理和 (OR) で指定することで、 eventfd() の振舞いを変更することができる。
バージョン 2.6.26 以前の Linux では、 flags 引き数は未使用であり、0 を指定しなければならない。
eventfd() は eventfd オブジェクトを参照するのに使用できる新しいファイルディスクリプター を返す。返されたファイルディスクリプターに対しては以下の操作を実行できる。
fork(2) で生成された子プロセスは、 eventfd() で生成されたファイルディスクリプターのコピーを継承する。 複製されたファイルディスクリプターは同じ eventfd オブジェクトに関連付けられる。 close-on-exec フラグが設定されていない場合、 execve(2) の前後で eventfd() で生成されたファイルディスクリプターは保持される。
成功すると、 eventfd() は新規の eventfd ファイルディスクリプターを返す。 エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。
eventfd() はカーネル 2.6.22 以降の Linux で利用可能である。 正しく動作する glibc 側のサポートはバージョン 2.8 以降で提供されている。 eventfd2() システムコール (「注意」参照) は カーネル 2.6.27 以降の Linux で利用可能である。 バージョン 2.9 以降では、glibc の eventfd() のラッパー関数は、カーネルが対応していれば eventfd2() システムコールを利用する。
eventfd() と eventfd2() は Linux 固有である。
アプリケーションは、パイプをイベントを通知するためだけに使用している 全ての場面において、パイプの代わりに eventfd ファイルディスクリプターを 使用することができる。 eventfd ファイルディスクリプターを使う方が、パイプを使う場合に比べて カーネルでのオーバヘッドは比べるとずっと小さく、ファイルディスクリプターも 一つしか必要としない (パイプの場合は二つ必要である)。
カーネル内で使用すると、eventfd ファイルディスクリプターはカーネル空間からユーザー空間へのブリッジ機能を提供することができ、 例えば KAIO (kernel AIO) のような機能が、あるファイルディスクリプターに何らかの操作が完了したことを 通知することができる。
eventfd ファイルディスクリプターの重要な点は、 eventfd ファイルディスクリプターが select(2), poll(2), epoll(7) を使って他のファイルディスクリプターと全く同様に監視できる点である。 このことは、アプリケーションは「従来の (traditional)」 ファイルの状態変化と eventfd インターフェースをサポートする他のカーネル機構の状態変化を同時に監視 できることを意味する (eventfd() インターフェースがない時には、これらのカーネル機構は select(2), poll(2), epoll(7) 経由で多重することはできなかった)。
下層にある Linux システムコールは二種類あり、 eventfd() と、もっと新しい eventfd2() である。 eventfd() は flags 引き数を実装していない。 eventfd2() では上記の値の flags が実装されている。 glibc のラッパー関数は、 eventfd2() が利用可能であれば、これを使用する。
GNU C
ライブラリは、eventfd
ファイルディスクリプターの読み出しと書き込みに
を関する詳細のいくつか抽象化するために、一つの型と、二つの関数を追加で
定義している。
typedef uint64_t eventfd_t; int eventfd_read(int fd, eventfd_t *value); int eventfd_write(int fd, eventfd_t value);
これらの関数は、eventfd ファイルディスクリプターに対する読み出しと 書き込みの操作を実行し、正しいバイト数が転送された場合には 0 を返し、そうでない場合は -1 を返す。
以下のプログラムは eventfd ファイルディスクリプターを生成し、 その後 fork を実行して子プロセスを生成する。 親プロセスが少しの間 sleep する間に、子プロセスは プログラムのコマンドライン引き数で指定された整数(列)をそれぞれ eventfd ファイルディスクリプターに書き込む。 親プロセスは sleep を完了すると eventfd ファイルディスクリプターから 読み出しを行う。
以下に示すシェルセッションにこのプログラムの使い方を示す。
$ ./a.out 1 2 4 7 14 Child writing 1 to efd Child writing 2 to efd Child writing 4 to efd Child writing 7 to efd Child writing 14 to efd Child completed write loop Parent about to read Parent read 28 (0x1c) from efd
#include <sys/eventfd.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> /* Definition of uint64_t */ #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) {
int efd, j;
uint64_t u;
ssize_t s;
if (argc < 2) {
fprintf(stderr, "Usage: %s <num>...\n", argv[0]);
exit(EXIT_FAILURE);
}
efd = eventfd(0, 0);
if (efd == -1)
handle_error("eventfd");
switch (fork()) {
case 0:
for (j = 1; j < argc; j++) {
printf("Child writing %s to efd\n", argv[j]);
u = strtoull(argv[j], NULL, 0);
/* strtoull() allows various bases */
s = write(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("write");
}
printf("Child completed write loop\n");
exit(EXIT_SUCCESS);
default:
sleep(2);
printf("Parent about to read\n");
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("read");
printf("Parent read %llu (0x%llx) from efd\n",
(unsigned long long) u, (unsigned long long) u);
exit(EXIT_SUCCESS);
case -1:
handle_error("fork");
} }
futex(2), pipe(2), poll(2), read(2), select(2), signalfd(2), timerfd_create(2), write(2), epoll(7), sem_overview(7)
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部である。 プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2014-07-08 | Linux |