next up previous contents
Next: ソケット Up: プロセス間通信をする Previous: ロック

シグナル

シグナルは非同期で相手プロセスに割り込んで同期を取る唯一の方法です。 同期通信はお互いに待ち合わせて次に進むといった、お互いのプロセスが同期 を取りに来ることを前提としたアルゴリズムを要求しますが、非同期の場合は 相手がどのようなコードを実行していようが気にせずに強制的に同期させるこ とが可能です。

強制的に割り込むといってももちろん割り込まれる側のプロセスで準備が出 来ていないといけません。この準備はシグナルの種類毎に用意する必要があり ます。準備をする関数はsignalです。

        #include        <signal.h>



        int     catch( sig, code, scp, addr )

        int     sig;

        int     code;

        struct sigcontext       * scp;

        char    * addr;

        {       /* ハンドラールーチン */        }



        /* ここはメインプログラムの中 */

        /* シグナルSIGALRMの処理をcatch関数にさせる*/

        signal( SIGALRM, catch );

        ...

        pause( );       /* シグナル受取を待つ。*/

これにより、以後このプロセスがどこを実行していてもSIGALRMが発生すると ハンドラールーチンcatchが呼び出されます。ただしこれは一度だけ有効で二 度目からはデフォールト処理に戻ってしまいます。繰り返しcatchを呼びたい ときはcatchのなかで同じようにsignalを呼び出す必要があります。

signalのハンドラールーチン(第2引数)にはユーザ関数の他にSIG_DFLと SIG_IGNの二つの値を取ることが出来ます。SIG_DFLはシステム標準の処理を呼 び、SIG_IGNはそのシグナルを無視します。SIGKILLシグナルとSIGSTOPシグナ ルだけは無視したりユーザ関数で捕獲したりは出来ません。シグナルの種類に ついてはman sigvecで見てください。

signalを呼び出して、ハンドラールーチンを宣言したプロセスがもう他にす ることがなくなったときはpause関数を呼び出してシグナルが発生するのを待 ちます。この場合ハンドラールーチンが呼び出された後、制御はプロセスに返 され、pauseのつぎから実行されます。

シグナルを発生する方法ですが、まず自分自身のプロセスにたいして、 abort( )でSIGA BRTを発生できます。またalarm(秒数)で「秒数」後に自分に 対しSIGALRMを発生できます。他のプロセスにシグナルを送るためには何らか の方法で相手のPID(プロセスID)を知っている必要があります。子プロセスに ついてはforkで知ることが出来ますが、まったく独立に発生したプロセスにつ いては自分でPIDを知る手段を用意する必要があります。

        kill( pid, SIGCHLD );

この例ではpidをPIDに持つプロセスにSIGCHLDシグナルを送ります。forkされ た子が親に例えば自分が終了することを通知するにもこの方法が使えます。子 は親に対して適当なシグナルを送って自分自身は終了します。親はシグナルを 受けて自分の子が終了しようとしていることを検出しwaitします。これにより 子プロセスはexitシステムコールでゾンビになった後ただちに親がwaitを発行 するのでリソースは開放されます。

        if( fork( ) == 0 )      /* 子プロセス */

        {

            /* 子にやらすべきことをここに書く */

            kill( getppid( ), SIGCHLD );        /* 親に通報して */

            exit( 0 );  /* 自分は終了 */

        }

        else            /* 親プロセス */

        {

            signal( SIGCHLD, catch );   /* 通知を受ける準備。関数定義は下*/

        }



        /* ハンドラールーチン。単に子をwaitする */

        void    catch( sig, code, scp, addr )

        int     sig;

        int     code;

        struct sigcontext * scp;

        char    * addr;

        {

                wait( 0 );      /* 子プロセスの終了を確認 */

        }

シグナルはシェルからkillコマンドを使って送ってやることも出来ます。

        % kill -HUP 2300        # プロセス2300にSIGHUPシグナルを送る。

        % kill -ALRM 2301       # プロセス2301にSIGALRMシグナルを送る。

        % kill 2302     # プロセス2302にSIGTERMシグナルを送る。

最後の例はデフォールトでSIGTERMシグナルが送られることを表わしています。 killコマンドでは-に続けてSIGをとったシグナル名を指定します。

8.7.時間管理のところでsetitimerシステムコールについて説明しました。 インターバルタイマーが0になる度に、ITIMER_REALの場合SIGALRMシグナルを、 ITIMER_VIRTUALの場合SIGVTALRMシグナルを発生します。これらのシグナルを キャッチできるようにしておけば一秒よりずっと細かい単位での繰り返し割り 込みが可能です。

シグナルは非同期的に割り込む手段を与えることはすでに述べましたが、も う一つ重要な側面は例外処理です。フォアグラウンドで走っているプロセスな どはどこでこけてもユーザはそれに対応できますが、DAQシステムなどで複数 のプロセスが走っている場合それぞれのプロセスが簡単に死んでしまうのでは 困ります。シグナルを受けても自動的に復帰したり、すくなくともほかの監視 プロセスに通知して終了するような出口処理を実行することが必要になります。 デーモンプロセスに実装されている例では例えばSIGHUPを受信するとプロセス は死ぬのではなくて再起動するように設計できます。これによりデーモンプロ セスに書き換えたコンフィギュレーションファイルを読み込ませたりすること があります。

シグナルを扱うときに他のシステムコールが発行されているときは注意が必 要です。UN IXの仕様ではシグナルを受け取ると完了を待っているすべてのシ ステムコールは中断されてユーザに制御が戻ってしまいます。この時のerrno の値はEINTRですから、シグナルによって中断されたサービスをユーザ自身が 再度発行しなければなりません。

        while( 1 )

        {

            st = read( fd, buf, sizeof( buf ) ); /* 中断されうるread */

            if( st == -1 )

            {

                if( errno != EINTR )    /* 本当のエラー */

                {

                    perror( 0 );        /* エラーを表示 */

                    break;      /* 無限ループから脱出 */

                }

                /* EINTRの時はなにもせずreadに戻る。*/

            }

            else

            {   /* 正常に読めた場合ここ */

            }

        }

最後にシグナルの種類についてまとめておきます。

        1 SIGHUP                ハングアップ

        2 SIGINT                割り込み

        3 SIGQUIT*              終了

        4 SIGILL*               不正命令

        5 SIGTRAP*              トレース・トラップ

        6 SIGABRT*              処理を中止する (abort))

        7 SIGEMT*               エミュレータ・トラップ

        8 SIGFPE*               浮動小数点演算例外

        9 SIGKILL               プロセス強制終了 (捕捉できない)

        10 SIGBUS*              バス・エラー

        11 SIGSEGV*             メモリーセグメント違反

        12 SIGSYS*              システム・コールに対する不適当な引数

        13 SIGPIPE              だれも読まないパイプに書き込み

        14 SIGALRM              アラーム・クロック

        15 SIGTERM              ソフトウェア終了シグナル

        16 SIGURG               ソケットの緊急事態

        17 SIGSTOP              停止 (捕捉できない)

        18 SIGTSTP              キーボードからの停止シグナル

        19 SIGCONT              停止後に継続

        20 SIGCHLD              子ステータスの変更

        21 SIGTTIN              バックグラウンドの端末からの読み込み

        22 SIGTTOU              バックグラウンドの端末への書き込み

        23 SIGIO                記述子上で入出力が可能

        24 SIGXCPU              cpu のタイム・リミット超過

        25 SIGXFSZ              ファイル・サイズの限界超過

        26 SIGVTALRM    仮想時間アラーム

        27 SIGPROF              タイマ・アラームのプロファイル

        28 SIGWINCH     ウィンドウの変更

        29 SIGLOST              資源の喪失

        30 SIGUSR1              ユーザ定義シグナル 1

        31 SIGUSR2              ユーザ定義シグナル 2

*がついているのはデフォルトでコアを吐くシグナルです。SIGKILLとSIGSTOP はsignalで宣言できません。



next up previous contents
Next: ソケット Up: プロセス間通信をする Previous: ロック



Kinya Hibino
Sun Jan 14 21:36:40 JST 1996