6. さらなる改造 2
6.2 プロセスのハンドラとの競合について
ハンドラをスレッドによって実現する方法は既に提示しました。ではスレッドとプロセス、おのおので用意したシグナルハンドラが競合する場合、どうなるのでしょうか。
下記、sigwait(3)とsigaction(2)の併用についてまとめてみましたが、実はこれは環境依存です。つまりどちらが勝つのかは仕様上未規定です(おそらく)。よって以下の結果は、私の環境上に限ります。
1.プロセス側ハンドラとスレッド側ハンドラで同じシグナルを捕捉対象にした場合
この場合、スレッド側ハンドラで捕捉されました。
/*
gcc signal_test_handle_thread.c -o signal_test_handle_thread -W-Wall -g -lpthread */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <signal.h> void signal_handler( int no ); void * signal_thread ( void * arg ); void * worker_thread ( void * arg ); int main( int argc, char ** argv ) { struct sigaction sa; sigset_t ss; pthread_t pt1; pthread_t pt2; int max_cnt; if( argc < 2 || atoi( argv[1] ) <= 0 ) { return 1; } sigemptyset( &ss ); sigaddset( &ss, SIGINT ); sigprocmask( SIG_BLOCK, &ss, 0 ); sa.sa_flags = SA_RESTART; sa.sa_handler = signal_handler; sigemptyset( &sa.sa_mask ); sigaction( SIGINT, &sa, 0 ); pthread_create( &pt1, 0, &signal_thread, 0 ); max_cnt = atoi( argv[1] ); pthread_create( &pt2, 0, &worker_thread, &max_cnt ); pthread_join( pt2, 0 ); pthread_cancel( pt1 ); return 0; } void * worker_thread( void * arg ) { int count; int max_cnt = *( int * )arg; for( count = 0; count < max_cnt; count ++ ) { sleep( 1 ); printf( "timer - [%d]\n", count ); } return 0; } void * signal_thread( void * arg ) { sigset_t ss; int sig; ( void )arg; pthread_detach( pthread_self( )); sigemptyset( &ss ); sigaddset( &ss, SIGINT ); pthread_sigmask( SIG_BLOCK, &ss, 0 ); while( 1 ) { if( sigwait( &ss, &sig )) { printf( "not SIGINT signal!!\n" ); continue; } if( sig == SIGINT ) { printf( "signal_thread 'SIGINT' Path!! [%d][%ld]\n", sig, pthread_self( )); } else { printf( "signal_thread 'not SIGINT' Path!! [%d][%ld]\n", sig, pthread_self( )); } } return 0; } void signal_handler( int no ) { ( void )no; char *mes = "signal_handler Path!!\n"; write( 1, mes, strlen( mes )); return; }
プロセス側ハンドラとスレッド側ハンドラで異なるシグナルを捕捉対象にした場合
スレッド側、プロセス側で対象のシグナルを捕捉できました。
/*
gcc signal_test_handle_thread_2.c -o signal_test_handle_thread_2-W -Wall -g -lpthread */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <signal.h> void signal_handler( int no ); void * signal_thread ( void * arg ); void * worker_thread ( void * arg ); int main( int argc, char ** argv ) { struct sigaction sa; sigset_t ss; pthread_t pt1; pthread_t pt2; int max_cnt; if( argc < 2 || atoi( argv[1] ) <= 0 ) { return 1; } sa.sa_flags = SA_RESTART; sa.sa_handler = signal_handler; sigemptyset( &sa.sa_mask ); sigaction( SIGTERM, &sa, 0 ); sigemptyset( &ss ); sigaddset( &ss, SIGINT ); sigprocmask( SIG_BLOCK, &ss, 0 ); pthread_create( &pt1, 0, &signal_thread, 0 ); max_cnt = atoi( argv[1] ); pthread_create( &pt2, 0, &worker_thread, &max_cnt ); pthread_join( pt2, 0 ); pthread_cancel( pt1 ); return 0; } void * worker_thread( void * arg ) { int count; int max_cnt = *( int * )arg; for( count = 0; count < max_cnt; count ++ ) { sleep( 1 ); printf( "timer - [%d]\n", count ); } return 0; } void * signal_thread( void * arg ) { sigset_t ss; int sig; ( void )arg; pthread_detach( pthread_self( )); sigemptyset( &ss ); sigaddset( &ss, SIGINT ); pthread_sigmask( SIG_BLOCK, &ss, 0 ); while( 1 ) { if( sigwait( &ss, &sig )) { printf( "not SIGINT signal!!\n" ); continue; } if( sig == SIGINT ) { printf( "signal_thread 'SIGINT' Path!! [%d][%ld]\n", sig, pthread_self( )); } else { printf( "signal_thread 'not SIGINT' Path!! [%d][%ld]\n", sig, pthread_self( )); } } return 0; } void signal_handler( int no ) { ( void )no; char *mes = "signal_handler Path!!\n"; write( 1, mes, strlen( mes )); return; }
スレッド側ハンドラを複数作成し、異なるシグナルを捕捉対象にした場合
おのおののハンドラで対象のシグナルを捕捉できました。
/*
gcc signal_test_handle_thread_3.c -o signal_test_handle_thread_3-W -Wall -g -lpthread */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <signal.h> void * signal_thread_1 ( void * arg ); void * signal_thread_2 ( void * arg ); void * worker_thread ( void * arg ); int main( int argc, char ** argv ) { sigset_t ss; pthread_t pt1; pthread_t pt2; pthread_t pt3; int max_cnt; if( argc < 2 || atoi( argv[1] ) <= 0 ) { return 1; } sigemptyset( &ss ); sigaddset( &ss, SIGINT ); sigaddset( &ss, SIGTERM ); sigprocmask( SIG_BLOCK, &ss, 0 ); pthread_create( &pt1, 0, &signal_thread_1, 0 ); pthread_create( &pt2, 0, &signal_thread_2, 0 ); max_cnt = atoi( argv[1] ); pthread_create( &pt3, 0, &worker_thread, &max_cnt ); pthread_join( pt3, 0 ); pthread_cancel( pt1 ); pthread_cancel( pt2 ); return 0; } void * worker_thread( void * arg ) { int count; int max_cnt = *( int * )arg; for( count = 0; count < max_cnt; count ++ ) { sleep( 1 ); printf( "timer - [%d]\n", count ); } return 0; } void * signal_thread_1( void * arg ) { sigset_t ss; int sig; ( void )arg; pthread_detach( pthread_self( )); sigemptyset( &ss ); sigaddset( &ss, SIGINT ); pthread_sigmask( SIG_BLOCK, &ss, 0 ); while( 1 ) { if( sigwait( &ss, &sig )) { printf( "not SIGINT signal!!\n" ); continue; } if( sig == SIGINT ) { printf( "signal_thread_1 'SIGINT' Path!! [%d][%ld]\n", sig, pthread_self( )); } else { printf( "signal_thread_1 'not SIGINT' Path!! [%d][%ld]\n", sig, pthread_self( )); } } return 0; } void * signal_thread_2( void * arg ) { sigset_t ss; int sig; ( void )arg; pthread_detach( pthread_self( )); sigemptyset( &ss ); sigaddset( &ss, SIGTERM ); pthread_sigmask( SIG_BLOCK, &ss, 0 ); while( 1 ) { if( sigwait( &ss, &sig )) { printf( "not SIGTERM signal!!\n" ); continue; } if( sig == SIGTERM ) { printf( "signal_thread_2 'SIGTERM' Path!! [%d][%ld]\n", sig, pthread_self( )); } else { printf( "signal_thread_2 'not SIGTERM' Path!! [%d][%ld]\n", sig, pthread_self( )); } } return 0; }
上記から、スレッド側のハンドラの方が優先して呼ばれていることが分かります。よって、スレッド側でハンドラを用意する場合は、誤解されかねないようにプロセス側のハンドラを用意しない方がよいでしょう。

-Wall -g -lpthread */