#include <sys/epoll.h>
epoll API は poll(2) と同様の処理を行う、つまり、複数のファイルディスク リプタを監視し、その中のいずれかが入出力可能な状態であるかを確認する。 epoll API は、エッジトリガインタフェースとレベルトリガインタフェースの いずれとしても使用することができ、監視するファイルディスクリプタの数が多い 場合にも使用できる。 epoll インスタンスの作成や管理を行うために 以下のシステムコールが提供されている:
epoll イベント配送 (distribution) インタフェースは、 エッジトリガ (ET) としてもレベルトリガ (LT) としても動作させることができる。 二つの配送機構の違いは、次のように説明できる。 このようなシナリオが起こったとしよう:
rfd ファイルディスクリプタが EPOLLET フラグ (エッジトリガ) を使って epoll に追加されていると、 利用可能なデータがファイル入力バッファにまだ存在するにもかかわらず ステップ 5 の epoll_wait(2) の呼び出しでハングする可能性がある。 その一方で、リモートの接続先 (peer) は既に送られたデータに 基づいて応答を期待しているかもしれない。 このようなことが起こる理由は、エッジトリガイベント配送では、 モニタしているファイルでイベントが起ったときにのみイベントが 配送されるためである。 したがって、ステップ 5 では、呼び出し側は結果的に 入力バッファ内にすで存在するデータを待つことになるかもしれない。 上記の例では、 2 で行われた書き込みによって rfd に関するイベントが生成され、 3 でイベントが消費 (consume) される。 4 で行われる読み込み操作では、全部のバッファデータを消費しないので、 ステップ 5 で行われる epoll_wait(2) の呼び出しが 無期限に停止 (block) するかもしれない。
EPOLLET フラグを採用するアプリケーションでは、 インタフェースはブロックしない (nonblocking) ファイルディスクリプタを 使うべきである。 これは、ブロックされる読み込みや書き込みによって、 複数のファイルディスクリプタを扱うタスクが 停止してしまうのを避けるためである。 epoll をエッジトリガ (EPOLLET) インタフェースとして使うために提案される方法は以下の通りである。
一方、レベルトリガインタフェースとして使う場合
(こちらがデフォルトである、
EPOLLET
が指定されなかった場合)、
epoll は単に高速な poll(2)
であり、使い方が同じなので、
poll(2)
が使われているところではどこでも使用することができる。
エッジトリガを使った場合でも、複数のデータを受信すると複数の epoll イベントが生成されるので、 呼び出し側には EPOLLONESHOT フラグを指定するオプションがある。 このフラグは epoll に対して、 epoll_wait(2) によるイベントを受信した後で、関連するファイルディスクリプタを無効にさせる。 EPOLLONESHOT フラグが指定された場合、 epoll_ctl(2) に EPOLL_CTL_MOD を指定してファイルディスクリプタを再度使用できるようにするのは、 呼び出し側の責任である。
epoll が消費するカーネルメモリの量を制限するために、 以下のインタフェースを使用することができる。
レベルトリガインタフェースとして使用するときの epoll の使い方は poll(2) と同じである。 しかしエッジトリガとして使う場合は、 アプリケーションのイベントループでストール (stall) しないように、 使い方をより明確にしておく必要がある。 この例では、リスナはブロックしないソケットであり、 listen(2) が呼ばれている。 関数 do_use_fd() は、 read(2) または write(2) によって EAGAIN が返されるまでは、新しい準備済みのファイルディスクリプタを使う。 イベント駆動ステートマシンアプリケーションは、 EAGAIN を受信した後、カレントの状態を記録しておくべきである。 これにより、次の do_use_fd() 呼び出しのときに、以前に停止したところから read(2) または write(2) を継続することができる。
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}
エッジトリガインタフェースとして使う場合、性能上の理由により、 一度 (EPOLLIN|EPOLLOUT) を指定してから (EPOLL_CTL_ADD で) ファイルディスクリプタを epoll インタフェースに追加することができる。 これにより、 epoll_ctl(2) に EPOLL_CTL_MOD を指定して呼び出すことで EPOLLIN と EPOLLOUT の連続的な切り替えが避けられる。
パケット指向やトークン指向のファイル (例えば、データグラムソケット、 canonical モードの端末) では、 読み込み用 / 書き込み用の I/O 空間の末尾を検知する唯一の方法は EAGAIN になるまで read/write を行うことである。
ストリーム指向のファイル (例えば、パイプ、FIFO、ストリームソケット) では、 読み込み用 / 書き込み用の I/O 空間が使い尽くされた状態は、 対象となるファイルディスクリプタから読み込んだデータ量または 書き込んだデータ量をチェックすることでも検知できる。 例えば、ある特定の量のデータを読み込むために read(2) を呼んだときに、 read(2) が返したバイト数がそれより少なかった場合、 そのファイルディスクリプタの読み込み用 I/O 空間が 使い尽くされたことが分かる。 write(2) を使って書き込みをするときも、同じことが言える (監視しているファイルディスクリプタが常にストリーム指向のファイルを 参照していることを保証できない場合には、後者の手法の使用を避けること)。
大きな I/O 空間がある場合、 その I/O 空間のデータを全て処理 (drain) しようとすると、 他のファイルが処理されず、飢餓を発生させることがある (この問題は epoll に固有のものではない)。
この問題の解決法は、準備済み状態のリストを管理して、 関連する data 構造体の中でファイルディスクリプタが 利用可能であるとマークすることである。 それによって、利用可能なすべてのファイルの中で どのファイルを処理する必要があるかを憶えることができ、 しかも順番に処理 (round robin) することができる。 既に利用可能であるファイルディスクリプタに対して それ以後に受け取るイベントを無視することもできる。
イベントキャッシュを使っている場合、 または epoll_wait(2) から返された全てのファイルディスクリプタを格納している場合、 クローズされたことを動的にマークする (つまり前のイベントの処理によってマークされる) 方法を提供すべきである。 epoll_wait(2) から 100 個のイベントを受け取り、 イベント #47 ではある条件でイベント #13 が閉じられると仮定する。 イベント #13 の構造体を削除しファイルディスクリプタを close(2) すると、イベントキャッシュはそのファイルディスクリプタを待つイベントが 存在するといって、混乱が起きる。
この問題を解決する 1 つの方法は、イベント 47 の処理をしている間に、 ファイルディスクリプタ 13 を削除して close(2) するために epoll_ctl(EPOLL_CTL_DEL) を呼び出し、関連付けられた data 構造体を削除済みとマークして、 クリーンアップリストにリンクすることである。 バッチ処理の中でファイルディスクリプタ 13 についての 他のイベントを見つけた場合、 そのファイルディスクリプタが以前に削除されたものであると分かるので、 混乱は起きない。
epoll API は Linux カーネル 2.5.44 に導入された。 glibc でのサポートはバージョン 2.3.2 で追加された。
epoll API は Linux 固有である。 他のシステムでも同様の機構が提供されている場合がある。 例えば、FreeBSD の kqueue や Solaris の /dev/poll などである。
epoll_create(2), epoll_create1(2), epoll_ctl(2), epoll_wait(2)
この man ページは Linux man-pages プロジェクトのリリース 3.54 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。