other versions
EPOLL(7) | Linux Programmer's Manual | EPOLL(7) |
名前¶
epoll - I/O イベント通知機能書式¶
#include <sys/epoll.h>説明¶
epoll API は poll(2) と同様の処理を行う、つまり、複数のファイルディスク リプタを監視し、その中のいずれかが入出力可能な状態であるかを確認する。 epoll API は、エッジトリガインタフェースとレベルトリガインタフェースの いずれとしても使用することができ、監視するファイルディスクリプタの数が多い 場合にも使用できる。 epoll インスタンスの作成や管理を行うために 以下のシステムコールが提供されている:- *
- epoll_create(2) は epoll インスタンスを作成し、そのインスタンスを参照する ファイルディスクリプタを返す。(もっと新しい epoll_create1(2) では、 epoll_create(2) の機能が拡張されている)。
- *
- 特定のファイルディスクリプタに対する監視内容を epoll_ctl(2) で登録する。 epoll インスタンスに現在登録されているファイルディスクリプタの集合は epoll 集合と呼ばれることもある。
- *
- epoll_wait(2) は I/O イベントを待つ。 現在利用可能な状態のイベントがなければ、呼び出したスレッドを停止する。
レベルトリガとエッジトリガ¶
epoll イベント配送 (distribution) インタフェースは、 エッジトリガ (ET) としてもレベルトリガ (LT) としても動作させることができる。 二つの配送機構の違いは、次のように説明できる。 このようなシナリオが起こったとしよう:- 1.
- パイプの読み込み側を表すファイルディスクリプタ ( rfd) が epoll インスタンスに登録される。
- 2.
- パイプへ書き込むプログラムが 2 kB のデータをパイプの書き込み側へ書き込む。
- 3.
- epoll_wait(2) を呼び出すと、読み込み可能 (ready) なファイルディスクリプタとして rfd が返る。
- 4.
- パイプから読み出すプログラムが、1 kB のデータを rfd から読み出す。
- 5.
- epoll_wait(2) の呼び出しが行われる。
(こちらがデフォルトである、 EPOLLET が指定されなかった場合)、 epoll は単に高速な poll(2) であり、使い方が同じなので、 poll(2) が使われているところではどこでも使用することができる。
/proc インタフェース¶
epoll が消費するカーネルメモリの量を制限するために、 以下のインタフェースを使用することができる。- /proc/sys/fs/epoll/max_user_watches (Linux 2.6.28 以降)
- このファイルは、あるユーザがシステム上の全ての epoll インスタンスに 登録できるファイルディスクリプタの総数の上限を規定する。 この上限は実ユーザ ID 単位である。 登録されたファイルディスクリプタ 1 つが消費するメモリ量は、 32 ビットカーネルでおよそ 90 バイト、 64 ビットカーネルでおよそ 160 バイトである。 現在のところ、 max_user_watches のデフォルト値は、利用可能なメモリ下限の 1/25 (4%) であり、 登録で消費されるメモリ量 (バイト単位) で割った値となる。
おすすめな使用例¶
レベルトリガインタフェースとして使用するときの 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); } } }
質問と解答¶
- Q0
- epoll 集合内の登録されたファイルディスクリプタを区別するには、 何をキーとして使えばよいか?
- A0
- キーはファイルディスクリプタ番号とオープンファイル記述 (open file description) の組である (オープンファイル記述は "open file handle" とも 呼ばれ、オープンされたファイルのカーネルの内部表現である)。
- Q1
- 1 つの epoll インスタンスに同じファイルディスクリプタを 2 回登録するとどうなるか?
- A1
- たぶん EEXIST を受け取るだろう。 しかしながら、同じ epoll インスタンスに対して複製されたディスクリプタを追加することは可能である ( dup(2), dup2(2), fcntl(2) F_DUPFD など)。 複製したファイルディスクリプタを異なる events マスクで登録すれば、イベントをフィルタリングするのに この機能は有用な手法である。
- Q2
- 2 つの epoll インスタンスが同じファイルディスクリプタを待ち受けることは可能か? もし可能であれば、イベントは両方の epoll ファイルディスクリプタに報告されるか?
- A2
- イベントは両方に報告される。 しかしながら、これを正しく扱うには注意深くプログラミングする必要が あるかもしれない。
- Q3
- epoll ファイルディスクリプタ自身は poll/epoll/select が可能か?
- A3
- 可能である。 epoll ファイルディスクリプタに処理待ちのイベントがある場合は、 読み出し可能だと通知されることだろう。
- Q4
- epoll ファイルディスクリプタを自身のファイルディスクリプタ集合に 入れようとするとどうなるか?
- A4
- epoll_ctl(2) の呼び出しは ( EINVAL で) 失敗するだろう。 ただし epoll ファイルディスクリプタを他の epoll ファイルディスクリプタ集合の内部に追加することは可能である。
- Q5
- epoll ファイルディスクリプタを UNIX ドメインソケットで他のプロセスに送ることは可能か?
- A5
- 可能だが、これをすることに意味はない。 なぜなら、受信側のプロセスが epoll 集合内のファイルディスクリプタのコピーを持っていないからである。
- Q6
- ファイルディスクリプタをクローズすると、そのファイルディスクリプタは全ての epoll 集合から自動的に削除されるか?
- A6
- 削除されるが、以下の点に注意が必要である。 ファイルディスクリプタはオープンファイル記述 ( open(2) 参照) への参照である。 ディスクリプタの複製を dup(2), dup2(2), fcntl(2) の F_DUPFD や fork(2) 経由で行う度に、同じオープンファイル記述を参照する新規のファイル ディスクリプタが生成される。 オープンファイル記述自体は、自身を参照する全てのファイルディスクリプタ がクローズされるまで存在し続ける。 ファイルディスクリプタが epoll 集合から削除されるのは、対応するオープンファイル記述を参照している 全てのファイルディスクリプタがクローズされた後である ( epoll_ctl(2) EPOLL_CTL_DEL を使ってそのディスクリプタを明示的に削除した場合にも削除される)。 このことは、 epoll 集合に属しているあるファイルディスクリプタをクローズした後であっても、 同じファイル記述を参照する他のファイルディスクリプタがオープンされている間は、 クローズしたファイルディスクリプタ宛にイベントが報告される可能性があると いうことを意味する。
- Q7
- 2 つ以上のイベントが epoll_wait(2) コールの間に発生した場合、それらはまとめて報告されるか、 それとも別々に報告されるか?
- A7
- まとめて報告されるだろう。
- Q8
- ファイルディスクリプタに対する操作は、 既に集められているがまだ報告されていないイベントに影響するか?
- A8
- 既存のファイルディスクリプタに対して 2 つの操作を行うことができる。 この場合、削除には意味がない。 変更すると、使用可能な I/O が再び読み込まれる。
- Q9
- EPOLLET フラグ (エッジトリガ動作) を使っている場合、 EAGAIN を受け取るまで、 継続してファイルディスクリプタを読み書きする必要があるか?
- A9
- epoll_wait(2)
からイベントを受け取ることは、
そのファイルディスクリプタが要求された
I/O
操作に対して準備済みである、
ということをユーザに示すものである。
次の
(ブロックしない) read/write
で EAGAIN
を受け取るまではファイルディスクリプタは準備済みであると
考えなければならない。
そのファイルディスクリプタをいつどのように使うかは、
全くユーザに任されてる。
ありがちな落とし穴と回避方法¶
- o 飢餓 (starvation) (エッジトリガ)
- o イベントキャッシュを使っている場合
バージョン¶
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.41 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。2012-04-17 | Linux |