SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ファイルディスクリプタについて

ファイルディスクリプタについて(3)
~タイマー用ディスクリプタ「timerfd」の特徴

第3回

類似の機能について

 類似した機能として、SIGALRMシグナルイベントの利用と、timer_create(2)の利用があります。

SIGALRMシグナルイベント

 以下は、SIGALRMを利用したプログラムです。

signal_alarm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>

void sig_handler( int sig_no );
int  limit_loop  = 0;
int  loop_count  = 0;

int main( int argc, char ** argv ) {

    struct sigaction sa;

    if( argc < 4 ) {
	fprintf( stderr, "Usage: %s <num> <num> <num>...\n", argv[0] );
	exit( EXIT_FAILURE );
    }

    struct itimerval val;
    val.it_interval.tv_sec  = atoi( argv[1] );
    val.it_interval.tv_usec = 0;
    val.it_value.tv_sec     = atoi( argv[2] );
    val.it_value.tv_usec    = 0;
    limit_loop = atoi( argv[3] );

    setitimer( ITIMER_REAL, &val, 0 );

    sa.sa_handler = sig_handler;
    sigemptyset( &sa.sa_mask );
    sigaction( SIGALRM, &sa, 0 );

    while( limit_loop != loop_count ) {
        pause( );
    }
    return 0;
}

void sig_handler( int sig_no ) {

    ( void )sig_no;

    static int sec = 0;
    printf( "%2d count passed.\n", sec );
    sec ++;
    loop_count ++;
}

 setitimer(2)を利用して、定期的にSIGALRMを発行させます。第1引数にITIMER_REALを設定することで、SIGALRMが呼ばれるようになります。更に、SIGALRMハンドラを用意して、SIGALRMが呼ばれた時の処理を行います。

 この方法の場合は、利用できるシグナルハンドラはSIGALRMのみなので、SIGALRM用のハンドラしか用意できず、必然的に1つの処理しか実装できません。アラームを利用した処理が1つしかない場合は、この方法はシンプルでよいと思います。

timer_createによるタイマー

 もう1つ、timer_create(2)を利用したアラーム処理です。こちらの場合はシグナルハンドラではなくコールバック関数を登録し、あらかじめ設定された時間が経過するとコールバック関数が非同期(具体的にはスレッドかシグナルハンドラ形式)で呼ばれます。どちらで呼ばれたいかはイベントを設定するパラメータで決まります。

 以下のサンプルソースはシグナル版です。スレッド版(timer_create_by_thread.c)はソースをダウンロードして参照ください。

timer_create_by_signal.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <wait.h>

#define SIGIO_READ  SIGRTMIN + 4
int cnt = 0;
#define TV2SEC(tv) ((double)((tv).tv_sec) + (double)((tv).tv_usec / 1000000.0))
void timer_handle( int signo, siginfo_t * info, void * ctx );

int main( int argc, char ** argv ) {

    if( argc < 2 ) {
	exit( 1 );
    }

    struct sigevent ev;
    ev.sigev_notify = SIGEV_SIGNAL;
    ev.sigev_signo  = SIGIO_READ;

    struct sigaction sa;
    sigemptyset( &sa.sa_mask );
    sa.sa_flags     = SA_SIGINFO;
    sa.sa_sigaction = timer_handle;
    sigaction( SIGIO_READ, &sa, 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;

    timer_t timer_id;
    timer_create( CLOCK_MONOTONIC,&ev, &timer_id );
    timer_settime( timer_id, 0, &ts, 0 );

    while( cnt < atoi( argv[1] )) {
	sleep( 1 );
    }

    return 0;
}

void timer_handle( int signo, siginfo_t * info, void * ctx ) {
    ( void )signo;
    ( void )ctx;

    if( info -> si_signo != SIGIO_READ ) {
	return;
    }

    static struct timeval  tv1,tv2;
    gettimeofday( &tv2, 0 );
    if( cnt == 0 ) {
	printf( "timer_thread %d Path...\n", cnt + 1 );
    }
    else {
	double rtn = TV2SEC( tv2 ) - TV2SEC( tv1 ) - 1;
	printf( "timer_thread %d Path... [%f]\n", cnt + 1, rtn );
    }
    tv1 = tv2;
    cnt ++;
}

 スレッドの場合は単にコールバック関数を用意すれば良いですが、シグナルの場合はコールバック関数に加えて、シグナルハンドラを用意する必要があります。その際に使用するシグナルイベントは、SIGRTMINからSIGRTMAXまで(私の環境では32から65までの33個)です。

 スレッドの場合、サーバの環境(メモリ等の資源)によって増減しますが、少なくとも数百個くらいの稼動は可能でしょう。その反面、スレッドの場合はコールバック関数を呼び出すための処理が、シグナルと比べて複雑です。

 サーバが高負荷状態の時、スレッドの場合は遅延が発生しやすいでしょう。シグナルの場合はカーネルの作り上シンプルな構成のため、遅延が発生しにくいです。この辺りの動作の違いによる高負荷時の性能については、本章の趣旨から外れるため、別のレポートで改めて解析させて下さい。

次のページ
pipe(2)との併用

この記事は参考になりましたか?

ファイルディスクリプタについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

(株)DTSに勤てます。WebアプリやJavaやLL等の上位アプリ環境を密かに憧れつつも、ず~っとLinuxとかHP-UXばかり、ここ数年はカーネル以上アプリ未満のあたりを行ったり来たりしています。mixiもやってまして、こちらは子育てとか日々の日記メインです。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4799 2010/02/12 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー