SHOEISHA iD

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

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

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

signalについて

signalについて(後篇)

シグナル使用上の注意点と改良ポイント


6. さらなる改造 1

 これまでシグナルのイベントを処理するのは、すべてシグナルハンドラで行ってました。しかしシグナルハンドラで処理する以上、最適化問題およびリエントラント問題という制限がつきまといます。

 ではシグナルハンドラを使わないでシグナルイベントを処理すれば問題は解決するのでしょうか。

 実は、ある特定の条件下でこの問題を緩和することができます。その方法はスレッドとsigwaitを併用するやり方です。

6.1 スレッドとの併用

  スレッドと併用する方法は下記の通りです。

  1. スレッドを生成する前に補足対象のシグナルイベントをマスクする。
  2. シグナルイベントを処理する専用スレッドを生成する。
  3. そのスレッドがシグナルイベントをsigwaitを使って見張る

 このようにする事で、非同期シグナルセーフではない関数を呼んでも問題が無くなります。さらに親プロセスではsleep(3)系などのpause(2)を行ってる関数を呼んでも問題ありません。

signal_test_thread.c
/*
gcc -g -W -Wall signal_test_thread.c -o signal_test_thread -lpthread
*/
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <signal.h> void * signal_handler( void * arg ); void * worker_thread ( void * arg ); int main( int argc, char ** argv ) { sigset_t ss ; pthread_t pt; int max_cnt; if( argc < 3 ) { return 0; } max_cnt = atoi( argv[1] ); /* SIGHUP,SIGINTのブロックマスク */ sigemptyset( &ss ); sigaddset( &ss, SIGHUP ); sigaddset( &ss, SIGINT ); sigprocmask( SIG_BLOCK, &ss, NULL ); /* SIGHUP,SIGINT処理用スレッド生成 */ pthread_create( &pt, NULL, &signal_handler, NULL ); pthread_detach( pt ); /* 実処理スレッド生成 */ pthread_create( &pt, NULL, &worker_thread, &max_cnt ); pthread_detach( pt ); sleep( atoi( argv[2] )); return 0; } void * signal_handler( void * arg ) { sigset_t ss ; int sig; ( void )arg; /* SIGHUP,SIGINTを待つ */ sigemptyset( &ss ); sigaddset( &ss, SIGHUP ); sigaddset( &ss, SIGINT ); while( 1 ) { if( sigwait( &ss, &sig )) { printf( "not SIGHUP,SIGINT signal!!\n" ); continue; } switch( sig ) { case SIGHUP: printf( "signal_handler 'SIGHUP' Path!! [%d]\n", sig ); break; case SIGINT: printf( "signal_handler 'SIGINT' Path!! [%d]\n", sig ); break; default: break; } } printf( "sigwait END!!\n" ); return NULL; } 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 NULL; }

 実行するとカウントを始めます。

$ ./signal_test_thread 100 200

 実行中にCtrl+C押下、または異なるターミナル等で下記のコマンドを実行する事で、signal_test_threadのpidに対してSIGHUPを送ることにより、画面にメッセージが表示されます。

$ kill -SIGHUP `ps -ef | grep signal_test_thread | grep -v grep | 
awk '{print $2 }'`

 signal_handlerはシグナルイベントを処理する専用スレッドです。signal_handler関数内のsigwait以降では、非同期シグナルセーフではない関数を呼び出しても問題無しというのがイベントハンドラと異なる所です。

 しかし、スレッド内での処理なのでスレッドセーフな関数を使用しなくてはいけないという制限が発生します。また、スレッド内で扱う変数をスレッド外で扱う場合でも同期対策(mutex等)を取らなくてはいけません。

 なお、sigwaitでは取得できない詳細情報を取得するための、sigwaitinfo版も記載します。こちらは送信元からシグナルと一緒に文字列も送信しています。正確にはポインタを受け取っていますが、プログラムではsigqueueを使い、スレッド側ではsigwaitinfoを使うことで詳細情報を取得できます。

signal_test_sigwaitinfo.c
/*
gcc signal_test_sigwaitinfo.c -o signal_test_sigwaitinfo -W -Wall -g -lpthread */
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <signal.h> #include <errno.h> void * worker_thread ( void * arg ); void * signal_handler( void * arg ); int main( int argc, char ** argv ) { sigset_t ss; pthread_t pt1, pt2; int max_cnt; if( argc < 2 ) { return 0; } max_cnt = atoi( argv[1] ); sigemptyset( &ss ); sigaddset( &ss, SIGHUP ); sigaddset( &ss, SIGINT ); sigprocmask( SIG_BLOCK, &ss, 0 ); pthread_create( &pt1, 0, &signal_handler, 0 ); pthread_detach( pt1 ); 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; union sigval val; val.sival_ptr = malloc( 256 ); for( count = 0; count < max_cnt; count ++ ) { sleep( 1 ) ; printf( "timer - [%d] [%d]\n", max_cnt, count ); sprintf( val.sival_ptr, "message from \'worker_thread\' count:[%d]", count ); sigqueue( getpid( ), SIGHUP, val ); } return 0; } void * signal_handler( void * arg ) { sigset_t ss; siginfo_t info; int sig; ( void )arg; sigemptyset( &ss ); sigaddset( &ss, SIGHUP ); sigaddset( &ss, SIGINT ); while( 1 ) { sig = sigwaitinfo( &ss, &info ); if( sig < 0 ) { printf( "error sigwaitinfo:[%d][%s]\n", errno, strerror( errno )); continue; } switch( sig ) { case SIGHUP: printf( "signal_handler 'SIGHUP' Path!! [%d] message:[%s]\n", sig, ( char * )info.si_ptr ); break; case SIGINT: printf( "signal_handler 'SIGINT' Path!! [%d]\n", sig ); break; default: break; } } printf( "sigwait END!!\n" ); return 0; }

次のページ
6. さらなる改造 2

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

signalについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1591 2008/08/20 15:57

イベント

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

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

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

メールバックナンバー