EPOLL
Section: Linux Programmer's Manual (7)
Updated: 2007-06-22
Index
JM Home Page
roff page
名前
epoll - I/O イベント通知機能
書式
#include <sys/epoll.h>
説明
epoll
は
poll(2)
の一種であり、エッジトリガインタフェースと
レベルトリガインタフェースのどちらとしても使用することができ、
監視するファイルディスクリプタの数が多い場合にも使用できる。
epoll
集合を設定したり制御したりするために、
次の 3 つのシステムコールが提供されている:
epoll_create(2),
epoll_ctl(2),
epoll_wait(2).
epoll
集合は
epoll_create(2)
で作成されるファイルディスクリプタに接続される。
ファイルディスクリプタに対する監視内容を
epoll_ctl(2)
で登録する。
最後に
epoll_wait(2)
で実際のイベント待ちを開始する。
レベルトリガとエッジトリガ
epoll
イベント配送 (distribution) インタフェースは、
エッジトリガ (ET) としてもレベルトリガ (LT) としても動作させることができる。
二つの配送機構の違いは、次のように説明できる。
このようなシナリオが起こったとしよう:
- 1.
-
パイプの読み込み側を表すファイルディスクリプタ
(rfd)
が
epoll
デバイスの内部に追加される。
- 2.
-
パイプへ書き込むプログラムが 2 kB のデータをパイプの書き込み側へ書き込む。
- 3.
-
epoll_wait(2)
を呼び出すと、読み込み可能 (ready) なファイルディスクリプタとして
rfd
が返る。
- 4.
-
パイプから読み出すプログラムが、1 kB のデータを
rfd
から読み出す。
- 5.
-
epoll_wait(2)
の呼び出しが行われる。
rfd
ファイルディスクリプタが
EPOLLET
フラグを使って
epoll
に追加されていると、
利用可能なデータがファイル入力バッファにまだ存在するにもかかわらず
ステップ
5
の
epoll_wait(2)
の呼び出しでハングする可能性がある。
その一方で、リモートの接続先 (peer) は既に送られたデータに
基づいて応答を期待しているかもしれない。
このようなことが起こる理由は、エッジトリガイベント配送では、
モニタしているファイルでイベントが起ったときにのみイベントが
配送されるためである。
したがって、ステップ
5
では、呼び出し側は結果的に
入力バッファ内にすで存在するデータを待つことになるかもしれない。
上記の例では、
2
で行われた書き込みによって
rfd
に関するイベントが生成され、
3
でイベントが消費 (consume) される。
4
で行われる読み込み操作では、全部のバッファデータを消費しないので、
ステップ
5
で行われる
epoll_wait(2)
の呼び出しが
無期限に停止 (block) するかもしれない。
EPOLLET
フラグ (エッジトリガ) を採用するアプリケーションでは、
インタフェースはブロックしない (non-blocking) ファイルディスクリプタを
使うべきである。
これは、ブロックされる読み込みや書き込みによって、
複数のファイルディスクリプタを扱うタスクが
停止してしまうのを避けるためである。
epoll
をエッジトリガ
(EPOLLET)
インタフェースとして使うために提案される方法は以下の通りである。
-
- i
-
ブロックしないファイルディスクリプタと共に使う。
- ii
-
read(2)
または
write(2)
が
EAGAIN
を返した後でのみ、イベントを待つ。
一方、レベルトリガインタフェースとして使う場合は、
epoll
は単に高速な
poll(2)
であり、使い方が同じなので、
poll(2)
が使われているところではどこでも使用することができる。
エッジトリガを使った場合でも、複数のデータを受信すると複数の
epoll
イベントが生成されるので、
呼び出し側には
EPOLLONESHOT
フラグを指定するオプションがある。
このフラグは
epoll
に対して、
epoll_wait(2)
によるイベントを受信した後で、関連するファイルディスクリプタを無効にさせる。
EPOLLONESHOT
フラグが指定された場合、
epoll_ctl(2)
に
EPOLL_CTL_MOD
を指定してファイルディスクリプタを再度使用できるようにするのは、
呼び出し側の責任である。
おすすめな使用例
レベルトリガインタフェースとして使用するときの
epoll
の使い方は
poll(2)
と同じである。
しかしエッジトリガとして使う場合は、
アプリケーションのイベントループでストール (stall) しないように、
使い方をより明確にしておく必要がある。
この例では、リスナはブロックしないソケットであり、
listen(2)
が呼ばれている。
関数 do_use_fd() は、
read(2)
または
write(2)
によって
EAGAIN
が返されるまでは、新しい準備済みのファイルディスクリプタを使う。
イベント駆動ステートマシンアプリケーションは、
EAGAIN
を受信した後、カレントの状態を記録しておくべきである。
これにより、次の do_use_fd() 呼び出しのときに、以前に停止したところから
read(2)
または
write(2)
を継続することができる。
struct epoll_event ev, *events;
for (;;) {
nfds = epoll_wait(kdpfd, events, maxevents, -1);
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listener) {
client = accept(listener, (struct sockaddr *) &local,
&addrlen);
if (client < 0){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d\n",
client);
return -1;
}
} else {
do_use_fd(events[n].data.fd);
}
}
}
エッジトリガインタフェースとして使う場合、性能上の理由により、
一度
(EPOLLIN|EPOLLOUT)
を指定してから
(EPOLL_CTL_ADD
で) ファイルディスクリプタを epoll インタフェースに追加することができる。
これにより、
epoll_ctl(2)
に
EPOLL_CTL_MOD
を指定して呼び出すことで
EPOLLIN
と
EPOLLOUT
の連続的な切り替えが避けられる。
質問と解答
- Q1
-
同じファイルディスクリプタを 1 つの epoll_set に 2 回追加するとどうなるか?
- A1
-
たぶん
EEXIST
を受け取るだろう。
しかし 2 つのスレッドが同じファイルディスクリプタを
2 回追加することは可能である。
これは無害な状態である。
- Q2
-
2 つの
epoll
セットが同じファイルディスクリプタを待ち受けることは可能か?
もし可能であれば、イベントは両方の
epoll
ファイルディスクリプタに報告されるか?
- A2
-
イベントは両方に報告される。
ただし、推奨されない。
またイベントは両方に報告される。
- Q3
-
epoll
ファイルディスクリプタ自身は poll/epoll/select が可能か?
- A3
-
可能である。
- Q4
-
epoll
ファイルディスクリプタを自身のファイルディスクリプタセットに入れると
どうなるか?
- A4
-
失敗するだろう。
ただし
epoll
ファイルディスクリプタを他の
epoll
ファイルディスクリプタセットの内部に追加することは可能である。
- Q5
-
epoll
ファイルディスクリプタを unix ソケットで他のプロセスに送ることは可能か?
- A5
-
不可能である。
- Q6
-
ファイルディスクリプタをクローズすると、そのファイルディスクリプタは全ての
epoll
セットから自動的に削除されるか?
- A6
-
削除される。
- Q7
-
2 つ以上のイベントが
epoll_wait(2)
コールの間に発生した場合、それらはまとめて報告されるか、
それとも別々に報告されるか?
- A7
-
まとめて報告されるだろう。
- Q8
-
ファイルディスクリプタに対する操作は、
既に集められているがまだ報告されていないイベントに影響するか?
- A8
-
既存のファイルディスクリプタに対して 2 つの操作を行うことができる。
この場合、削除には意味がない。
変更すると、使用可能な I/O が再び読み込まれる。
- Q9
-
EPOLLET
フラグ (エッジトリガ動作) を使っている場合、
EAGAIN
を受け取るまで、
継続してファイルディスクリプタを読み書きする必要があるか?
- A9
-
その必要はない。
epoll_wait(2)
からイベントを受け取ることは、
「そのファイルディスクリプタが要求された I/O 操作に対して準備済みである」
ということをユーザに示すものである。
「次の
EAGAIN
を受け取るまではファイルディスクリプタは準備済みである」
と単純に考えるべきである。
そのようなファイルディスクリプタをいつどのように使うかは、
全くユーザに任されてる。
また読み込み用 / 書き込み用 I/O 空間が使い尽くされた状態は、
対象となるファイルディスクリプタから読み込んだデータ量または
書き込んだデータ量をチェックすることで検知できる。
例えば、ある特定の量のデータを読み込むために
read(2)
を呼んだときに、
read(2)
が返したバイト数がそれより少なかった場合、
そのファイルディスクリプタの読み込み用 I/O 空間が
使い尽くされたことが分かる。
write(2)
を使って書き込みをするときも、同じことが言える。
ありがちな落とし穴と回避方法
- o 飢餓 (starvation) (エッジトリガ)
-
大きな I/O 空間がある場合、
その I/O 空間のデータを全て処理 (drain) しようとすると、
他のファイルが処理されず、飢餓を発生させることがある。
これは
epoll
に固有のものではない。
この問題の解決法は、準備済み状態のリストを管理して、
関連する data 構造体の中でファイルディスクリプタが
利用可能であるとマークすることである。
それによって、利用可能なすべてのファイルの中で
どのファイルを処理する必要があるかを憶えることができ、
しかも順番に処理 (round robin) することができる。
既に利用可能であるファイルディスクリプタに対して
それ以後に受け取るイベントを無視することもできる。
- o イベントキャッシュを使っている場合
-
イベントキャッシュを使っている場合、
または
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(7)
は Linux カーネル 2.5.44 に導入された新しい API である。
インタフェースは Linux カーネル 2.5.66 で確定されるべきである。
準拠
epoll API は Linux 固有である。
他のシステムでも同様の機構が提供されている場合がある。
例えば、FreeBSD の
kqueue
や Solaris の
/dev/poll
などである。
関連項目
epoll_create(2),
epoll_ctl(2),
epoll_wait(2)
Index
- 名前
-
- 書式
-
- 説明
-
- レベルトリガとエッジトリガ
-
- おすすめな使用例
-
- 質問と解答
-
- ありがちな落とし穴と回避方法
-
- バージョン
-
- 準拠
-
- 関連項目
-
This document was created by
man2html,
using the manual pages.
Time: 04:31:37 GMT, November 19, 2007