Edited at

eventfdの使い方

More than 1 year has passed since last update.

epollと合わせて使うと嬉しい系FDの一つeventfdの使い方をちょっと纏めてみる

実のところ、こいつはかなり癖があるので使えるポイントが限られそう…


従来の処理

想定されている使い方はpipeを使ってイベント通知だけをする場合の置き換えです。


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にしたところで、余り代わりはありません。


eventfd+epollにしてみる

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カウンティング

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指定をする場合は…


eventfdでカウントを通知

/**

* 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はキューでも出来る)


関連

signalfdの使い方

timerfdの使い方