mokky14's IT diary

IT関係の仕事メモ、勉強会の感想など書いてます。

C言語での定周期処理の実装(timer_settime使用版)

setitimerが廃止予定関数ということだったので、timer_settime関数を使用した定周期処理も作ってみた。
前のソースを改造して作成。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>

int main() {
        // setitimerにより通知されるシグナルをマスク
        sigset_t sigset;
        sigemptyset(&sigset);
        sigaddset(&sigset, SIGALRM);
        pthread_sigmask(SIG_BLOCK, &sigset, NULL);

        // タイマ設定 5秒毎に通知
        timer_t         timerid;
        const struct timespec interval = { 5, 0 };
        const struct itimerspec itimer = {interval, interval};
        int ret = timer_create(CLOCK_REALTIME, NULL, &timerid);
        if( ret == -1) {
                perror("timer_create");
                return -1;
        }
        ret = timer_settime(timerid, TIMER_ABSTIME, &itimer, NULL);
        if ( ret != 0 ) {
                perror("timer_settime");
                return -1;
        }

        int i;
        for ( i=0; i<10; i++ ) {
                int signo;
                sigwait(&sigset, &signo);

                struct timeval tv;
                gettimeofday(&tv, NULL);
                char    timebuf[26];
                ctime_r(&tv.tv_sec, timebuf);
                *strchr(timebuf, '\n') = '\0';
                printf("[%s] [%06d] signal[%d] recieved.\n",
                        timebuf, tv.tv_usec, signo);

                // たまに時間かかる処理がある想定
                if ( i % 4 == 3 ) {
                        unsigned int waittime = 7;
                        printf("wait %d seconds.\n", waittime);
                        sleep(waittime);
                }
        }

        ret = timer_delete(timerid);
        if ( ret != 0 ) {
                perror("timer_delete");
                return -1;
        }

        return 0;
}

実行。

[~]$ cc timer2.c -lpthread -lrt
[~]$ ./a.out
[Wed Apr  3 18:12:29 2013] [380927] signal[14] recieved.
[Wed Apr  3 18:12:30 2013] [000964] signal[14] recieved.
[Wed Apr  3 18:12:35 2013] [001200] signal[14] recieved.
[Wed Apr  3 18:12:40 2013] [001446] signal[14] recieved.
wait 7 seconds.
[Wed Apr  3 18:12:47 2013] [001920] signal[14] recieved.
[Wed Apr  3 18:12:50 2013] [000928] signal[14] recieved.
[Wed Apr  3 18:12:55 2013] [001189] signal[14] recieved.
[Wed Apr  3 18:13:00 2013] [001413] signal[14] recieved.
wait 7 seconds.
[Wed Apr  3 18:13:07 2013] [001828] signal[14] recieved.
[Wed Apr  3 18:13:10 2013] [000898] signal[14] recieved.
[~]$ 

シグナル通知による定周期処理なので、シグナル受信側がシグナル受信可能にならないと処理は動かない。
シグナル送信は、シグナル受信側が受信してるかどうかに関わらず行われる模様。

(追記)
よく考えたら、この方式、以下の問題がある。

  • SIGALRMはプロセスに配信されるので、プロセス全体としてSIGALRMのブロックが必要。
  • SIGALRMを受信するスレッドは1スレッドに限定する必要がある。(複数スレッドで同一シグナルのwaitを行うと、どのスレッドに配信されるか分からない)
  • SIGALRMを受信する側で何のイベントに対する通知かを知る術がない。よって、1プロセスの中で複数のタイマ設定を行うことは出来ない。

3つ目は致命的なので、この方式は使い物にならなそうだな。。

追加で調べてみたら、timerfd_settimeというAPIがあって、これなら1プロセスの複数イベント設定も可能そう。
と思ったら、新しいkernelから導入されたAPIらしく、CentOS5.9には入ってなかった。。