sa_handlerを使った一般的なシグナルハンドラ処理
3.1 sa_handler
sa_handlerを使ったサンプルは前ページで示した通りですが、sa_handlerを使った一般的なシグナルハンドラ処理は以下のようになります(なお、以下のプログラムはBSDシグナルを模倣しています)。
/*
gcc -g -W -Wall signal_test_handler.c -o signal_test_handler
*/ #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( ) { char buf[256]; int ret; struct sigaction sa; sa.sa_handler = signal_handler; sa.sa_flags = SA_RESTART; /* SIGINTをキャッチ */ if( sigaction( SIGINT, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error! [%d][%s]\n", errno, strerror( errno )); exit( 1 ); } /* SIGHUPをキャッチ */ if( sigaction( SIGHUP, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error! [%d][%s]\n", errno, strerror( errno )); exit( 1 ); } while( 1 ) { memset( buf, '\0', sizeof( buf )); printf( "read wait..\n" ); ret = read( 0, buf, sizeof(buf) ); if( ret <= 0 ) { fprintf( stderr, "read error!\n" ); exit( 1 ); } write( 1, buf, ret ); } return 0; } void signal_handler( int no ) { ( void )no; char *mes1 = "signal get\n"; char *mes2 = "signal end\n"; write( 1, mes1, strlen( mes1 )); sleep( 2 ); write( 1, mes2, strlen( mes2 )); }
POSIXシグナルを扱う場合、特に詳細情報が必要ない場合はsa_handlerを使います。
3.2 sa_sigaction
旧シグナルもsa_handlerに登録したシグナルハンドラも引数が1つで、その値はシグナルのイベントの値でした。これは複数のシグナルイベントに共通のハンドラを設定した場合など、ハンドラ内でSwitch~case文など等の分岐をする際に使用します。
しかし、時にはそれ以上の情報を知りたい場合もあります。その際に使用するハンドラを設定するのがsa_sigactionです。このハンドラを使用する場合、sa_flagsにSA_SIGINFOを加える必要があります。
sa_sigactionは 3つの引数を取ります。
void (*sa_sigaction)(int, siginfo_t *, void *);
第1引数はイベントコード、第2引数であるsiginfo_tは下記の要素を持つ構造体です。
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void * si_ptr; /* POSIX.1b signal */
void * si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}
si_signo、si_errno、si_codeは、シグナルイベントの値に関わらず設定されます。それ以外の項目は、シグナルイベントの値によって設定される項目が異なるようです。また、si_codeはさらに詳細な理由が設定されるようです。
第3引数は、ハードウェア・コンテキストのスナップショットです。これはさまざまな構造体のポインタを含みます。シグナルハンドラが呼ばれた時点でのシステム内で見つかったすべてのレジスタ値に関するデータを参照できます。
これらの構造体には、汎用レジスタ(GPR)、浮動小数点レジスタ(FPR)、VMXレジスタ(使用可能な場合)、特殊目的レジスタ(SPR)など、ほぼすべてのレジスタに関するデータが含まれています。当然、アーキテクチャ固有情報です。
以下はsa_sigactionを使用して「signal_test_handler.c」と同等の動きをするプログラムです。情報を詳細に表示するようにしています。
/*
gcc -g -W -Wall signal_test_sigaction.c -o signal_test_sigaction
*/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> void signal_handler( int no, siginfo_t * info, void * ctx ); int main( ) { char buff[256]; int ret; struct sigaction sa; sa.sa_sigaction = signal_handler; sa.sa_flags = SA_SIGINFO; /* sa_sigaction 使用 */ sa.sa_flags |= SA_RESTART; /* SIGINTをキャッチ */ if( sigaction( SIGINT, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error!\n" ); exit( 1 ); } /* SIGHUPをキャッチ */ if( sigaction( SIGHUP, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error!\n" ); exit( 1 ); } while( 1 ) { printf( "read wait..\n" ); ret = read( 0, buff, sizeof( buff )); write( 1, buff, ret ); } return 0; } void signal_handler( int no, siginfo_t * info, void * ctx ) { /* 本当はダメ */ fprintf( stdout, "no :[%d]\n", no ); /* 本当はダメ */ fprintf( stdout, "si_signo:[%d]\n", info -> si_signo ); /* 本当はダメ */ fprintf( stdout, "si_errno:[%d]\n", info -> si_errno ); /* 本当はダメ */ fprintf( stdout, "si_code :[%d]\n", info -> si_code ); /* 本当はダメ */ fprintf( stdout, "si_pid :[%d]\n", info -> si_pid ); /* 本当はダメ */ fprintf( stdout, "si_uid :[%d]\n", info -> si_uid ); /* 本当はダメ */ fprintf( stdout, "ctx :[%p]\n", ctx ); }
これを実行したターミナル上でCtrl+Cを押下すると、
no :[2] si_signo:[2] si_errno:[0] si_code :[128] si_pid :[0] si_uid :[0] ctx :[0x7fbffff460]
となり、別のターミナル上でコマンドkill -HUP `ps -ef | grep signal_test_sigaction | grep -v grep | awk '{print $2}'`実行すると、
no :[1] si_signo:[1] si_errno:[0] si_code :[0] si_pid :[17033] <- コマンドを実行したターミナルの pid,および uid si_uid :[1004] ctx :[0x7fbffff460]
といった結果が私の環境では得られました、
なお、sa_sigaction宛に詳細情報を送る事も可能です。その方法は次回説明します。
