CodeZine(コードジン)

特集ページ一覧

signalについて(前篇)

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

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2007/10/04 14:00

目次

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にて一般的なシグナルハンドラ処理を行ってみます。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

修正履歴

  • 2011/01/21 00:40 SIGFPE -> SIGSEGV に修正しました。

  • 2007/10/11 22:27 P2 "サスペンド状態でブロックしている時" -> "入力待ち状態の時" "libc-2以前" -> "libc-2になる前"

バックナンバー

連載:signalについて

著者プロフィール

  • 赤松 エイト(エイト)

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

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5