LinuxFDイベント系のAPIまとめ

今回libeventやlibevといった新しいイベント系APIを使う機会が出てきたので、まとめておきます。
深堀すると[こうなる](https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748)のが目に見えてるので、簡単な紹介だけ。

##**ここでいうイベントの流れ**

1. ファイルディスクリプタというものを作る。
2. 待ち受け側はこのファイルディスクリプタでイベントを監視する。
3. 通知側はこのファイルディスクリプタにイベントを通知する。
4. 通知されたイベントを2が検知し、処理が実行される。

以降**FDと書かれていたらファイルディスクリプタ**のことと思ってください。

スレッド間、プロセス間等でお話しするイメージですね。ものによってはイベント監視や通知自体をラップして使用者に意識させない作りになっています。
例えば…

- libeventというOSSはイベント検知時に呼ばれる関数を登録する形式なので、**利用者はイベント検知時に呼ばれる関数だけ作ればイベント処理実装完了！**
- timerfdというAPIはライブラリが勝手にタイマーイベントを発行してくれるので、**待ち受け側だけ作ればタイマー処理実装完了！**

こういったことは高級言語には当たり前かもしれませんが、OSSの用意もあり、自作でガリガリチューンアップも出来るFDイベントはC言語らしい機能だと思います。

##**1. ファイルディスクリプタ**

色々あります。FDのイベント発行側が充実しているため、後で紹介するイベント監視が充実しているのか、それともその逆か？興味がある方は歴史を調べると面白いかもですね。

|API|概要|備考|
|:---|:---|:---|
|[open](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/open.2.html)|/dev/console(標準出力)なんかはopenしてやりとりでしたね。確か。|[通常ファイルに対してのみ行われるべき操作をデバイスファイルに対して行わない](https://www.jpcert.or.jp/m/sc-rules/c-fio32-c.html)|
|[socket](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/socket.7.html)|通信処理でまず習うであろうAPI。送受信に関するオプションがsetsockoptで色々と出来るので奥が深い(覗きはしない|こういったオプションはOSS内で色々利用されているので、OSSコードを見ると思わぬ発見が|
|[socketpair](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/socketpair.2.html)|socketをサクッと使いたい時におすすめ。API一発で通信できます。|双方向通信出来る半面、2個FDを使うのが難点|
|[eventfd](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/eventfd.2.html)|ただただ通知をするためだけのAPI。カウンター式で、send時は数を送信(0だとFD未反応)、read側はカウンターが読める。|FD1つでいいので片方向ならこれを工夫して使った方がいいかも|
|[timerfd](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/timerfd_create.2.html)|一定時間後にイベントを発行してくれる超絶便利API|timerfdがない環境があるなら自作した方がいいですよレベルで好き|
|[signalfd](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/signalfd.2.html)|シグナルを拾うためのFDを作成する。mainの待ち受け内でシグナルも拾えるのは便利ですね。|[シグナルの定義](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html),シグナルハンドラーは制約があることに注意。 [signalfdの参考](https://qiita.com/ksworks/items/2e62b2b43b2bd6d06e90)|
|[mq_open](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/mq_open.3.html)|メッセージキューファイル用のopen処理|read/writeも専用APIで行います。|
|[pipe](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/pipe.2.html)|forkしたプロセス間通信でも使用可能なFDを作成するAPI|forkで利用できる代わりに単方向通信です。|

※メッセージキュー:ファイル名を決めておいて、そのファイルを待ち受け側送信側互いにmq_openすれば通信が可能になる仕組み。
　[UNIXドメインソケット](https://ja.wikipedia.org/wiki/UNIX%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88)でも似たようなことが出来ます。
※[fcntl](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/fcntl.2.html)等でoptionの変更が可能。[blocking/non-blocking設定の参考](http://d.hatena.ne.jp/s-kita/20080608/1212891524)



##**2. イベント監視**

###**受信機能**

|API|概要|
|:---|:---|
|[read](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/read.2.html), [revc](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/recv.2.html), etc|基本的なファイルディスクリプタの読み込み|
|[mq_revc](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/mq_receive.3.html)|メッセージキューファイル経由での受信が可能なAPI。|

###**イベント監視系機能**

単純に受信をするだけだと1FDに1APIとなるので非効率。なのでFDをまとめて監視する機能があると便利。この機能が充実しています。

|API|概要|個人的感想|
|:---|:---|:---|
|[select, pselect](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/select.2.html)|古き良き定番API, Windowsにも対応|明確にFDを意識して利用するのがいい。毎回FD_ISSET等のAPIで確認作業をしなければいけないのが面倒|
|[epoll](https://linuxjm.osdn.jp/html/LDP_man-pages/man7/epoll.7.html)|selectの改良版、常にFDを設定しなおす手間を無くしたもの|EPOLL_CTL_ADDすれば勝手に待ち受けにFDが追加されるので楽。個人の趣味としてはselectとどっこい|
|[libevent](http://man7.org/linux/man-pages/man3/event.3.html)|上記のようなイベント監視系のAPIをラップし、シンプルに使えるようにしたOSS|コールバック形式なのが使いやすい。ただ、スレッドセーフでないためか繊細な印象。[簡単なまとめ記事](https://qiita.com/developer-kikikaikai/items/69f889e03133441ffbe0)|
|[libev](https://github.com/enki/libev)|上記の高速対応版。[API参考](https://qiita.com/janus_wel/items/51bbd7517ab237a0b65c)。ドキュメントはREADME⇒[ここ](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod)。<br>まずは「WHAT TO READ WHEN IN A HURRY」を、詳細は「FUNCTIONS CONTROLLING EVENT LOOPS」を参考にしてください。<br>ドキュメント類も充実しています。GPLで問題なければこちらを選択されるのが良さそう。|マルチスレッドで使用する場合はev_default_loop(0)ではなくev_loop_new, ev_loop, ev_unloopを使用します。|

※poll, kqueue等は利用したことがないため省略

上記サンプルについて、色々あると思いますがlighttpdのfdevent_XXX.cを見るのがオススメです。全部まとまってますので。
OSSコードは多環境サポートの為複数処理を書くことになるため、各機能の保証付き実装が見ることが出来ます。

##**3. イベント発行**


|API|概要|
|:---|:---|
|[write](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/write.2.html), [send](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/send.2.html)|基本的なファイルディスクリプタの書き込み|
|[mq_send](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/mq_send.3.html)|メッセージキューファイル経由での送信が可能なAPI|

mqは別ですが、それ以外のFDはwriteが使えます。また、timerfdがFD兼イベント発行となっています。

##**その他**

2018/06/19追記
FDじゃないけど[pthread_cond](https://linuxjm.osdn.jp/html/glibc-linuxthreads/man3/pthread_cond_wait.3.html)なんてのもありますね。
mutexと組み合わせて使うので、既にmutexで排他している時にはいいかもです。

##参考

多重 I/Oに関する歴史 https://itkq.jp/blog/2017/05/10/linux-file-and-io/
各リンク