愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

個人的epollめも

仕事柄、「ああいうデカいサイズのファイルアップロードとか、preforkでやるべきじゃないよね。イベントモデルじゃないと厳しい」とかそんな話をすることがあって、「確かにpreforkだとあの場合すぐプロセスが埋まっちゃいますよね」とか自分も分かったようなことを言うのだけども、裏側のことは特にはっきりと理解しているわけでなく、イベントモデルだと1プロセスで大丈夫だとか、下手にブロックしちゃうと死んじゃうとか、聞きかじっただけの知識でそういうことを何となく語ってしまう自分に不意に背筋がゾッとする、そのようなことも秋の夜長にはございます。

そのような良心の問題とは特に関係なく、ネットを漂流してほどよいサンプルを見つけたのでふんわりとした自分用のメモ。

http://www.oreilly.co.jp/community/blog/2010/03/pthread-epoll-inet-server-part1.html

http://www.oreilly.co.jp/community/blog/2010/03/pthread-epoll-inet-server-part2.html

全体的に

epfdごとに、監視されるfdのリストみたいなものがひもづく(これはおそらくカーネル空間に実際にある)。fdのリストに対して、epoll_ctlで監視したいfdを追加/削除などする。epoll_waitにepoll_eventの構造体のポインタを渡す。epoll_waitはイベントが起こったfdの数を返すので、それを使って先程epoll_waitに渡したポインタ(配列)をループして対応した処理をする。(この「対応した処理」がイベントに対するコールバックとみなせる)

epoll_create -> epoll_ctl -> epoll_waitがメインの流れだが、epoll_waitによるイベントループの処理過程でepoll_ctlで監視リストに新しいfdが追加されるとか、既存のfdを削除するとかも当然ある。


以下、理解していない点

* epfdと通常のfdの空間が別なのかどうか

epoll_create(int size)

epoll用のfdを返す。以下、これを使ってepoll_waitやらepoll_ctlをコールする。sizeは現在は無視されるらしい。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

opでfdの追加やら削除やらを指定する。fdは仮想的なfdリストに追加したいfdそのもの。サンプルを見る限りではevent.data.fdにもfdを突っ込むのが普通っぽいので、なんだかそこが冗長な気もする。event.eventsで監視したいイベントの種類を指定する。

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

めんどくさいので省略、というか「全体的に」に書いた


以下、ぼんやりと考えたこと

* AnyEventのAE::cv->recvって、while(1) { nfd = epoll_wait(); } なイベントループに突入するためのものだとすると納得できる(もちろん内部的にはlibevでラップされているはずだし、これがそのまま正解というのはありえない)

* nginxってworkerの数の設定とかあるけど、参考記事の二番目にあるようなfdをプロセス間で受け渡してからepoll_waitするようなモデル(5epoll-multi.c)なのか、それとも単に1プロセスでやるモデル(4epoll.c)なのか。あるいはマルチプロセスだけど5epoll-multi.cとはまた違うモデルなのか。

* 規模は違えどeventモデルのサーバがwhile(1) { epoll_wait(); } のような形のイベントループを中心に据えて作られるものだとすれば、そのどこかにずーっとブロックするような動作が入ってしまうとよろしくない、のは納得できる(だって中身はwhileループだから…)