SHOEISHA iD

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

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

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

signalについて

signalについて(前篇)

シグナルの概要と一般的な処理方法


3.POSIXシグナル

 前項で説明した混乱状況や現状のシグナルの拡張性の無さを解消するため、UNIXの互換性を保証する機関であるPOSIXでは新しいシグナルシステムコールを定めました。新しいシステムコールでは関数名も変えてしまい、オプション次第でSysVもBSDもどちらの動作も模倣できるようにしました。

 signal(2)の代わりに使うシステムコールはsigaction(2)です。

#include <signal.h>
int sigaction( int signum,      /* シグナル番号(イベントコード) */
    struct sigaction * act,     /* 変更後の情報 */
    struct sigaction * oact );  /* 変更前の情報 */

 引数actoactはNULLを許容します。変更前の設定情報が必要ない場合は、oactにNULLをセットします。

#include <signal.h>
struct sigaction {
    union {
        /* シグナルハンドラ */
        void ( *_sa_handler )( int );
       /* シグナルハンドラ。3引数に対応 */
        void ( *_sa_sigaction)( int, siginfo_t *, void * );
    } _u;
    /* シグナルマスク */
    sigset_t sa_mask;
    /* 動作の詳細を設定するフラグ */
    unsigned long sa_flags;
    /* 廃止されたため使用してはならない。 */
    void     ( *sa_restorer )( void );
}

#define sa_handler   _u._sa_handler
#define sa_sigaction _u._sa_sigaction

 sa_handlerとsa_sigactionは、unionで定義されているため、どちらか一方しか設定できません。sa_flagsは、シグナル・ハンドラの動作を変更するためのフラグの論理和をとった値を指定します。わりとよく知られているフラグは下記の通りです。

SA_RESETHAND シグナルハンドラが1回呼ばれたら、シグナルの動作をデフォルトに戻します。
SA_NODEFER シグナルハンドラ内にいる時に同一シグナルを受けると、そのままハンドラが叩かれます。
SA_RESTART システムコールはシグナルハンドラの処理を待ち、処理が終わればシステムコールの処理を継続します。
SA_ONSTACK アプリケーションで用意したシグナル・スタックでハンドラを呼び出す事を宣言します。中篇4.6参照。
SA_SIGINFO sa_sigactionの使用を宣言します。当オプションを設定しない時はsa_handlerを使用します。次ページ3.2参照。
SA_NOCLDWAIT signam(イベントコード)がSIGCHLDの時、子プロセスが終了した時に子プロセスをゾンビにさせません。中篇4.5参照。

 普通はSA_RESTARTフラグはデフォルトでセットします。これをしないとシステムコールを使っている所はすべてループで囲み、errno==EINTREの時にcontinueする処理を加えなくてはいけません。

 以下はCtrl+Cを1回押下すると、「read error,Interrupted system call」が出力されてプログラムが終了するサンプルプログラムです。

signal_test_restart.c
/*
gcc -g -W -Wall signal_test_restart.c -o signal_test_restart
*/
#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; if( sigaction( SIGINT, &sa, NULL ) != 0 ) { /* SIGINTをキャッチ */ fprintf( stderr, "sigaction(2) error!\n" ); 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! [%d][%s]\n", errno, strerror( errno )); 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 )); }

 SA_RESETHANDフラグはハンドラが叩かれるのを1回のみ許可し、2回目以降は叩かれないようにする場合に使用します。

 次のサンプルプログラムは、Ctrl+Cを1回押下するとハンドラが起動しますが、2回目はプログラムが終了します。これは、Ctrl+Cによって発生するシグナル(SIGINT)のデフォルトの動作が「プログラムの終了」だからです。

signal_test_resethand.c
/*
gcc -g -W -Wall signal_test_resethand.c -o signal_test_resethand
*/
#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; sa.sa_flags |= SA_RESETHAND; if( sigaction( SIGINT, &sa, 0 ) != 0 ) { /* SIGINTをキャッチ */ fprintf( stderr, "sigaction(2) error!\n" ); 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! [%d][%s]\n", errno, strerror( errno )); 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 )); }

 SA_NODEFERフラグはハンドラ内の処理中でも同一シグナルを受け付けるので、実際にシグナルが発生した回数を記録できます(それくらいしか利用価値はないように思います……)。

 以下のサンプルプログラムは、ハンドラ内でCtrl+Cを押下すると「signal get」が出力され、2秒後に「signal end」が出力しますが、その2秒間にCtrl+Cを押下すると押下した分だけ「signal get」が出力されます。

signal_test_nodefer.c
/*
gcc -g -W -Wall signal_test_nodefer.c -o signal_test_nodefer
*/
#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; sa.sa_flags |= SA_NODEFER; if( sigaction( SIGINT, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error!\n" ); 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! [%d][%s]\n", errno, strerror( errno )); 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 )); }

 SA_RESTARTフラグは旧シグナルで言うところのBSD系OSを、また、SA_RESETHANDフラグとSA_NODEFERフラグはSysVシグナルを模倣できます。個人的な見解ですが、今となってはSysVシグナルを模倣する理由がほとんど無いので、使う理由も無いのかな、と思っています。

 sa_sigactionの説明は後で行います。まずはsa_handlerにて一般的なシグナルハンドラ処理を行ってみます。

次のページ
sa_handlerを使った一般的なシグナルハンドラ処理

修正履歴

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

signalについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1450 2011/01/21 00:40

イベント

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

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

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

メールバックナンバー