epoll
と合わせて使うと嬉しい系FDの一つeventfd
の使い方をちょっと纏めてみる
実のところ、こいつはかなり癖があるので使えるポイントが限られそう…
従来の処理
想定されている使い方はpipe
を使ってイベント通知だけをする場合の置き換えです。
int pFD[2];
bool done = false;
/**
* initEvent - イベント通知用pipe生成
*/
void initEvent (void)
{
pipe (gFD);
}
/**
* notifyEvent - イベント発生を通知
*/
void notifyEvent (void)
{
int event = 1;
write (pFD[1], &event, sizeof(event));
}
/**
* receiveEvent - イベント受信の回収
*/
void receiveEvent (void)
{
int event = 0;
read (pFD[0], &event, sizeof(event));
}
/**
* signalTerminate - シグナルハンドラ
*/
void signalTerminate (int signum)
{
done = true;
notifyEvent ();
}
ここではeventの内容は使用しません。
select
/poll
などでの待ち合わせを起こすためだけに使われるパターンがeventfd
に対応する使用方法です。
ということで、シグナル受信によるハンドラなどから呼び出すような場合を想定します。
※write
はAsync-Signal-Safeという意外な事実w
グローバル変数の値を変更して、それを通知するだけ…とかですね。
eventfd + epoll
これをeventfd
にしたところで、余り代わりはありません。
int eventFD;
bool done = false;
/**
* initEvent - イベント通知用pipe生成
*/
void initEvent (void)
{
eventFD = eventfd (0, 0);
}
/**
* signalTerminate - シグナルハンドラ
*/
void signalTerminate (int signum)
{
done = true;
eventfd_write (eventFD, 1);
}
※明示的にAsync-Signal-Safeではないけども、write
同様ということでeventfd_write
もシグナルハンドラで使って良いと思ってる
ここで嬉しい点は殆ど無いのですが…
-
pipe
に対するread
遅れでの詰まりが無い - ディスクリプタ数が 1/2 になるのでファイル数に制限があるなら(
eventfd
使える環境での制限…)
カウント伝播の為の使い方(EFD_SEMAPHORE)
eventfd
カウンターの読み書きが出来ることから、スレッド/プロセス間でカウントを伝えるためにも使用することが出来ます。
一つはread
側でカウント計上する感じで、
eventfd_t event_count;
/**
* countEventsByFD - eventfdでのカウンティング
* ※EFD_SEMAPHORE未指定の場合
*/
void countEventsByFD ()
{
eventfd_t new_count = 0;
eventfd_read (eventFD, &new_count);
event_count += new_count;
}
もう一つ、EFD_SEMAPHORE指定をする場合は…
/**
* initEventSemaphore - イベント通知用pipe生成(セマフォ)
*/
void initEventSemaphore (void)
{
eventFD = eventfd (0, EFD_NONBLOCK|EFD_SEMAPHORE);
}
/**
* workerLoop - ワーカスレッドなどでのイベントループ
*/
void workerLoop (void)
{
while (!done) {
struct epoll_event event = {0};
int evnum = epoll_wait (epollFD, &event, 1, -1);
for (int i=0; i<evnum; ++i) {
if (event.data.fd == eventFD) {
eventdf_t count = 0;
eventfd_read (eventFD, &count);
if (count > 0) {
doEvent ();
}
}
/* else if (event->data.fd == any_other_fd) { ... } */
}
}
return ;
}
eventfd_read
が必ず1を取得するので、eventfd_write
側から受信スレッド側の処理数を伝えたら、workerスレッド側で1つずつ拾って処理をするような感じになりますね。
それ以外にデータを通知したい場合
カウンタ以外に何かしらのデータを通知したい場合は、eventfd
では無くてmq_open
でメッセージキューを作りましょう(select
/poll
はキューでも出来る)