eventfd -
イベント通知用のファイルディスクリプタを生成する
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
eventfd() は "eventfd
オブジェクト"
を生成する。 eventfd
オブジェクトはユーザ空間アプリケーションがイベント待ち受け/通知用の
仕組みとして使うことができる。また、カーネルがユーザ空間アプリケーションに
イベントを通知するためにも使うことができる。
このオブジェクトには、unsigned
の 64 ビット整数 (
uint64_t)
型のカウンタが含まれており、このカウンタはカーネルにより管理される。
このカウンタは
initval
引き数で指定された値で初期化される。
以下の値のいくつかをビット単位の論理和
(OR) で指定することで、
eventfd()
の振舞いを変更することができる。
- EFD_CLOEXEC (Linux 2.6.27 以降)
- 新しいファイルディスクリプタに対して
close-on-exec ( FD_CLOEXEC)
フラグをセットする。
このフラグが役に立つ理由については、
open(2) の O_CLOEXEC
フラグの説明を参照のこと。
- EFD_NONBLOCK (Linux 2.6.27 以降)
- 新しく生成されるオープンファイル記述
(open file description) の O_NONBLOCK
ファイルステータスフラグをセットする。
このフラグを使うことで、
O_NONBLOCK
をセットするために
fcntl(2)
を追加で呼び出す必要がなくなる。
- EFD_SEMAPHORE (Linux 2.6.30 以降)
- Provide semaphore-like semantics for reads from the new
file descriptor. See below.
バージョン 2.6.26 以前の Linux
では、
flags
引き数は未使用であり、0
を指定しなければならない。
eventfd() は eventfd
オブジェクトを参照するのに使用できる新しいファイルディスクリプタ
を返す。返されたファイルディスクリプタに対しては以下の操作を実行できる。
- read(2)
- read(2)
は成功すると、8
バイトの整数を返す。
渡されたバッファの大きさが
8 バイト未満の場合、
read(2) はエラー EINVAL
で失敗する。
- The value returned by read(2) is in host byte order,
i.e., the native byte order for integers on the host machine.
- The semantics of read(2) depend on whether the
eventfd counter currently has a nonzero value and whether the
EFD_SEMAPHORE flag was specified when creating the eventfd file
descriptor:
- *
- EFD_SEMAPHORE
が指定されておらず、eventfd
カウンタが 0
以外の値 の場合、
read(2)
はカウンタ値を格納した
8 バイトの値を返し、
カウンタ値は 0
にリセットされる。
- *
- EFD_SEMAPHORE
が指定されていて eventfd
カウンタが 0
以外の値の場合、
read(2) は値 1 の 8
バイト値を返し、カウンタ値は
1 減算される。
- *
- read(2)
を呼び出した時点で
eventfd カウンタが 0
の場合、 read(2)
はカウンタが 0
以外になるまで停止
(block) する (0
以外になった時点で
read(2)
は上記で述べた通り実行を再開する)、
もしくはファイルディスク
リプタが非停止 (nonblocking)
に設定されている場合はエラー
EAGAINで 失敗する。
- write(2)
- write(2)
は、引き数のバッファで渡された
8
バイトの整数値をカウンタに加算する。
カウンタに格納可能な最大値は
unsigned の 64
ビット整数の最大値から
1 を引いた値
(すなわち 0xfffffffffffffffe)
である。
加算を行うとカウンタ値が最大値を超過する場合には、
そのファイルディスクリプタに対して
read(2)
が実行されるまで、
write(2) は停止 (block) する、
もしくはファイルディスクリプタが非停止
(nonblocking)
に設定されている場合はエラー
EAGAIN で失敗する。
- 渡されたバッファの大きさが
8
バイト未満の場合、もしくは
値 0xffffffffffffffff
を書き込もうとした場合、
write(2) はエラー EINVAL
で失敗する。
- poll(2), select(2)
(と同様の操作)
- 返されたファイルディスクリプタは、
poll(2) (epoll(7) も同じ) や
select(2)
をサポートしており、以下のような動作をする。
- *
- カウンタが 0
より大きい値の場合、
ファイルディスクリプタは読み出し可能となる
( select(2) の readfds
引き数や poll(2) の POLLIN
フラグ)。
- *
- 少なくとも値
"1" を、停止 (block)
を伴わずに書き込める場合、
ファイルディスクリプタは書き込み可能となる
( select(2) の writefds
引き数や poll(2) の POLLOUT
フラグ)。
- *
- カウンタ値のオーバーフローが検出された場合、
select(2)
はファイルディスクリプタは読み出し可能と書き込み可能の両方を通知し、
poll(2) は POLLERR
イベントを返す。
上述の通り、 write(2)
でカウンタがオーバーフローすることは決してない。
しかしながら、 KAIO
サブシステムによって
2^64 回の eventfd "signal posts" が
実行された場合にはオーバーフローが起こり得る
(理論的にはあり得るが、実用的にはあり得ない)。
オーバーフローが発生した場合、
read(2) は uint64_t の最大値
(すなわち 0xffffffffffffffff)
を返す。
- eventfd
ファイルディスクリプタは、これ以外のファイルディスクリプタ
多重 API である pselect(2),
ppoll(2), epoll(7)
もサポートしている。
- close(2)
- ファイルディスクリプタがそれ以降は必要なくなった際には、クローズすべきである。
同じ eventfd
オブジェクトに関連付けられたファイルディスクリプタが全て
クローズされると、そのオブジェクト用の資源がカーネルにより解放される。
fork(2)
で生成された子プロセスは、
eventfd()
で生成されたファイル
ディスクリプタのコピーを継承する。
複製されたファイルディスクリプタは同
じ eventfd
オブジェクトに関連付けられる。
close-on-exec
フラグが設定されていない場合、
execve(2) の前後で
eventfd()
で生成されたファイルディスクリプタは保持される。
返り値¶
成功すると、
eventfd()
は新規の eventfd
ファイルディスクリプタを返す。
エラーの場合、-1
を返し、
errno
にエラーを示す値を設定する。
エラー¶
- EINVAL
- An unsupported value was specified in flags.
- EMFILE
- オープン済みのファイルディスクリプタの数がプロセスあたりの上限に
達していた。
- ENFILE
- オープン済みのファイル総数がシステム全体の上限に達していた。
- ENODEV
- (カーネル内の)
無名 inode
デバイスをマウントできなかった。
- ENOMEM
- 新しい eventfd
ファイルディスクリプタを生成するのに十分なメモリがなかった。
バージョン¶
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
のシステムコール¶
下層にある Linux
システムコールは二種類あり、
eventfd()
と、もっと新しい
eventfd2() である。
eventfd() は
flags
引き数を実装していない。
eventfd2() では上記の値の
flags
が実装されている。 glibc
のラッパー関数は、
eventfd2()
が利用可能であれば、これを使用する。
glibc の追加機能¶
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.40 の一部
である。プロジェクトの説明とバグ報告に関する情報は
http://www.kernel.org/doc/man-pages/
に書かれている。