pipe(2)との併用
前述した、timer_create(2)とpipe(2)を併用することで、timerfd(2)とほぼ同一の機能を実装できます。シグナルハンドラもしくはコールバック関数内でpipe(2)をwrite(2)し、メインプロセスでは多重I/O等でpipe(2)のread(2)を待ちます。タイマー発生時、pipeを経由してメインプロセスが検知し、しかるべき処理を行う、という流れです。
以下のサンプルソースはスレッド版、多重I/Oはselect(2)で実装しました。もちろんシグナル版でも実装可能です。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <wait.h>
int pipefd[2];
#define TV2SEC(tv) ((double)((tv).tv_sec) + (double)((tv).tv_usec / 1000000.0))
void timer_thread( sigval_t val );
int main( int argc, char ** argv ) {
if( argc < 2 ) {
exit( 1 );
}
int max_cnt = atoi( argv[1] );
struct timeval tv1,tv2;
sigval_t sig;
int i;
timer_t timer_id;
struct sigevent ev;
ev.sigev_notify = SIGEV_THREAD;
ev.sigev_value.sival_ptr = &timer_id;
ev.sigev_notify_function = timer_thread;
ev.sigev_notify_attributes = 0;
struct itimerspec ts;
ts.it_value.tv_sec = 1;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 1;
ts.it_interval.tv_nsec = 0;
pipe( pipefd );
timer_create( CLOCK_MONOTONIC, &ev, &timer_id );
timer_settime( timer_id, 0, &ts, 0 );
fd_set rset, org_rset;
FD_ZERO( &org_rset );
FD_SET( pipefd[0], &org_rset );
for( i = 0; i < max_cnt; i ++ ) {
rset = org_rset;
int rtn = select( FD_SETSIZE, &rset, 0, 0, 0 );
if( rtn < 0 && errno == EINTR ) {
continue;
}
read( pipefd[0], &sig, sizeof( sigval_t ));
gettimeofday( &tv2, 0 );
if( i ) {
printf( "timer_thread %d Path... [%f]\n",
i, TV2SEC( tv2 ) - TV2SEC( tv1 ));
}
tv1 = tv2;
}
return 0;
}
void timer_thread( sigval_t val ) {
write( pipefd[1], &val, sizeof( sigval_t ));
}
まとめ
timerfd(2)は、既存機能であるteimer_create(2)に勝るとも劣らない、非常に利用価値の高い機能だと思います。
timer_create(2)ではコールバック関数が非同期で呼び出されますが、timerfd(2)はコールバック関数を用意する必要がなく、ファイルディスクリプタを複数管理したり、非同期で監視することも容易です。コールバック関数内では複雑な処理をするには制限もありますが、ファイルディスクリプタで管理する場合はその制限も外れます。
代替案で提示した pipe(2) を利用する案でも十分運用は可能と思われますが、pipe用変数のスコープがグローバルになることから、管理が煩雑になりがちで、この点を踏まえても timerfd(2) は高い有用性を備えていると考えています。
定期間隔で処理を行う際、有効に利用できる機能でしょう。
次回は、プログラムの内外部から発行されたシグナルイベントを補足し、通知するためのシグナル用ディスクリプタ「signalfd」について説明します。
