CodeZine(コードジン)

特集ページ一覧

signalについて(中篇)

シグナルの実装

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2007/10/10 14:00

目次

4. シグナルの実装(タイマー、ポーズ)

4.3 タイマー

 システム側から定期的な間隔でシグナルを発行させ、そのシグナルを補足する事でタイマーを実現できます。その際に補足するシグナルは、SIGALRMです。SIGALRMを「定期的に発生させる」機能に、setitimerがあります。

 詳細はman等で見てもらいたいですが、一般的なタイマーとして使用する場合は第1引数にITIMER_REALを設定し、引数にタイマー間隔を設定することで、定期的にSIGALRMを発行します。

signal_test_timer.c
/*
gcc -g -W -Wall signal_test_timer.c -o signal_test_timer
*/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <sys/time.h> void sig_handler( int sig_no ); int while_count = 0; int limit_loop = 0; int loop_count = 0; int main( int argc, char ** argv ) { struct itimerval val; struct sigaction sa; if( argc < 4 ) { return 1; } timerclear( &val.it_interval ); timerclear( &val.it_value ); val.it_interval.tv_sec = atoi( argv[1] ); val.it_interval.tv_usec = atoi( argv[2] ); val.it_value.tv_sec = atoi( argv[1] ); val.it_value.tv_usec = atoi( argv[2] ); 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 ) { while_count ++; } timerclear( &val.it_interval ); timerclear( &val.it_value ); setitimer( ITIMER_REAL, &val, 0 ); return 0; } void sig_handler( int sig_no ) { ( void )sig_no; static int sec = 0; printf( "%2d sec passed! %d\n", sec, while_count ); sec ++; while_count = 0; loop_count ++; }

 その他にもITIMER_VIRTUALおよびITIMER_PROFがありますが、使用されているのを見たことが無いです。個人的な使用方法としては、定期的にその時の状況を共有ファイル等に書き出し、そのファイルをSNMP等に参照させる、といった使い方をしていました。

4.4 ポーズ

 下記のプログラムを実行してみてください。適当なタイミングでCtrl+Cを押下するとハンドラからメッセージが表示された後、プログラムが終了します。

signal_test_sleep.c
/*
gcc -g -W -Wall signal_test_sleep.c -o signal_test_sleep
*/
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> void signal_handler( int no ); int main( ) { struct sigaction sa; int ret; sa.sa_handler = signal_handler; sigemptyset( &sa.sa_mask ); sa.sa_flags = SA_RESTART; sigaction( SIGINT, &sa, 0 ); ret = sleep( 3600 ); fprintf( stdout, "ret:[%d][%d][%s]\n", ret, errno, strerror( errno )); return 0; } void signal_handler( int no ) { ( void )no; char *mes = "signal get\n"; write( 1, mes, strlen( mes )); }

 sleep(3)関数の引数は十分に大きな値であり、シグナルの設定でもSA_RESTARTが設定されているにもかかわらずプロセスが終了してしまいます。sleep(3)の戻り値を見ると、EINTR(Interrupted system call)が返ってきます。

 4.1.2と似ていますが、sleep(3)系関数(sleep(3)、usleep(3)、nanosleep(2)、alarm(2)、select(2)等)は、その内部でpause(2)関数とalarm(2)関数を使っていると思われます。pause(2)関数はシグナルを受信するまでサスペンドし続け、シグナルを受信しシグナルハンドラが処理を終えた後、「-1」を返してerrnoにEINTRを設定し、サスペンドを解除します。

signal_test_pause.c
/*
gcc -g -W -Wall signal_test_pause.c -o signal_test_pause
*/
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> void signal_handler( int no ); int main( ) { struct sigaction sa; int ret; sa.sa_handler = signal_handler; sa.sa_flags = SA_RESTART; sigemptyset( &sa.sa_mask ); sigaction( SIGINT, &sa, 0 ); ret = pause( ); fprintf( stdout, "ret:[%d][%d][%s]\n", ret, errno, strerror( errno )); return 0; } void signal_handler( int no ) { ( void )no; char *mes = "signal get\n"; write( 1, mes, strlen( mes )); }

 シグナルハンドラとsleep(3)系関数を両方使う場合、指定した時間まで待ってくれない場合があるという事です。

 対策としては、errno == EINTRが成り立つ時に、再度sleep(3)系関数を実行するくらいでしょう。

 下記は対策を施したプログラムです。

signal_test_new_sleep.c
/*
gcc -g -W -Wall signal_test_new_sleep.c -o signal_test_new_sleep
*/
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> void signal_handler( int no ); int main( ) { struct sigaction sa; int ret; sa.sa_handler = signal_handler; sigemptyset( &sa.sa_mask ); sa.sa_flags = SA_RESTART; sigaction( SIGINT, &sa, 0 ); ret = 3600; while( 1 ) { ret = sleep( ret ); fprintf( stdout, "ret:[%d][%d][%s]\n", ret, errno, strerror( errno )); if( ret != 0 && errno == EINTR ) { continue; } break; } return 0; } void signal_handler( int no ) { ( void )no; char *mes = "signal get\n"; write( 1, mes, strlen( mes )); }

 また、nanosleep(2)を使用すると、EINTRによってサスペンドが解除された場合の残りのサスペンド時間をより詳細に取得できます。

signal_test_nano_sleep.c
/*
gcc -g -W -Wall signal_test_nano_sleep.c -o signal_test_nano_sleep
*/
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> #include <time.h> void signal_handler( int no ); int main( ) { struct sigaction sa; struct timespec req, rem; int ret; sa.sa_handler = signal_handler; sigemptyset( &sa.sa_mask ); sa.sa_flags = SA_RESTART; sigaction( SIGINT, &sa, 0 ); req.tv_sec = 60; req.tv_nsec = 0; rem.tv_sec = 0; rem.tv_nsec = 0; while( 1 ) { ret = nanosleep( &req, &rem ); fprintf( stdout, "ret:[%d][%d][%s] sec:[%ld]\n", ret, errno, strerror( errno ), rem.tv_sec ); if( ret != 0 && errno == EINTR ) { req = rem; rem.tv_sec = 0; rem.tv_nsec = 0; continue; } break; } return 0; } void signal_handler( int no ) { ( void )no; char *mes = "signal get\n"; write( 1, mes, strlen( mes )); }

  • LINEで送る
  • このエントリーをはてなブックマークに追加

修正履歴

  • 2007/11/17 00:46 signal_test_new_sleep.c if( errno == EINTR ) -> if( ret != 0 && errno == EINTR ) signal_test_nano_sleep.c if( errno == EINTR ) -> if( ret != 0 && errno == EINTR )

バックナンバー

連載:signalについて

著者プロフィール

  • 赤松 エイト(エイト)

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

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5