SHOEISHA iD

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

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

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

signalについて

signalについて(前篇)

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


sa_handlerを使った一般的なシグナルハンドラ処理

3.1 sa_handler

 sa_handlerを使ったサンプルは前ページで示した通りですが、sa_handlerを使った一般的なシグナルハンドラ処理は以下のようになります(なお、以下のプログラムはBSDシグナルを模倣しています)。

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

 POSIXシグナルを扱う場合、特に詳細情報が必要ない場合はsa_handlerを使います。

3.2 sa_sigaction

 旧シグナルもsa_handlerに登録したシグナルハンドラも引数が1つで、その値はシグナルのイベントの値でした。これは複数のシグナルイベントに共通のハンドラを設定した場合など、ハンドラ内でSwitch~case文など等の分岐をする際に使用します。

 しかし、時にはそれ以上の情報を知りたい場合もあります。その際に使用するハンドラを設定するのがsa_sigactionです。このハンドラを使用する場合、sa_flagsにSA_SIGINFOを加える必要があります。

 sa_sigactionは 3つの引数を取ります。

void (*sa_sigaction)(int, siginfo_t *, void *);

 第1引数はイベントコード、第2引数であるsiginfo_tは下記の要素を持つ構造体です。

siginfo_t {
    int      si_signo;    /* Signal number */
    int      si_errno;    /* An errno value */
    int      si_code;     /* Signal code */
    pid_t    si_pid;      /* Sending process ID */
    uid_t    si_uid;      /* Real user ID of sending process */
    int      si_status;   /* Exit value or signal */
    clock_t  si_utime;    /* User time consumed */
    clock_t  si_stime;    /* System time consumed */
    sigval_t si_value;    /* Signal value */
    int      si_int;      /* POSIX.1b signal */
    void *   si_ptr;      /* POSIX.1b signal */
    void *   si_addr;     /* Memory location which caused fault */
    int      si_band;     /* Band event */
    int      si_fd;       /* File descriptor */
}

 si_signo、si_errno、si_codeは、シグナルイベントの値に関わらず設定されます。それ以外の項目は、シグナルイベントの値によって設定される項目が異なるようです。また、si_codeはさらに詳細な理由が設定されるようです。

 第3引数は、ハードウェア・コンテキストのスナップショットです。これはさまざまな構造体のポインタを含みます。シグナルハンドラが呼ばれた時点でのシステム内で見つかったすべてのレジスタ値に関するデータを参照できます。

 これらの構造体には、汎用レジスタ(GPR)、浮動小数点レジスタ(FPR)、VMXレジスタ(使用可能な場合)、特殊目的レジスタ(SPR)など、ほぼすべてのレジスタに関するデータが含まれています。当然、アーキテクチャ固有情報です。

 以下はsa_sigactionを使用して「signal_test_handler.c」と同等の動きをするプログラムです。情報を詳細に表示するようにしています。

signal_test_sigaction.c
/*
gcc -g -W -Wall signal_test_sigaction.c -o signal_test_sigaction
*/
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> void signal_handler( int no, siginfo_t * info, void * ctx ); int main( ) { char buff[256]; int ret; struct sigaction sa; sa.sa_sigaction = signal_handler; sa.sa_flags = SA_SIGINFO; /* sa_sigaction 使用 */ sa.sa_flags |= SA_RESTART; /* SIGINTをキャッチ */ if( sigaction( SIGINT, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error!\n" ); exit( 1 ); } /* SIGHUPをキャッチ */ if( sigaction( SIGHUP, &sa, 0 ) != 0 ) { fprintf( stderr, "sigaction(2) error!\n" ); exit( 1 ); } while( 1 ) { printf( "read wait..\n" ); ret = read( 0, buff, sizeof( buff )); write( 1, buff, ret ); } return 0; } void signal_handler( int no, siginfo_t * info, void * ctx ) { /* 本当はダメ */ fprintf( stdout, "no :[%d]\n", no ); /* 本当はダメ */ fprintf( stdout, "si_signo:[%d]\n", info -> si_signo ); /* 本当はダメ */ fprintf( stdout, "si_errno:[%d]\n", info -> si_errno ); /* 本当はダメ */ fprintf( stdout, "si_code :[%d]\n", info -> si_code ); /* 本当はダメ */ fprintf( stdout, "si_pid :[%d]\n", info -> si_pid ); /* 本当はダメ */ fprintf( stdout, "si_uid :[%d]\n", info -> si_uid ); /* 本当はダメ */ fprintf( stdout, "ctx :[%p]\n", ctx ); }

 これを実行したターミナル上でCtrl+Cを押下すると、

no      :[2]
si_signo:[2]
si_errno:[0]
si_code :[128]
si_pid  :[0]
si_uid  :[0]
ctx     :[0x7fbffff460]

 となり、別のターミナル上でコマンドkill -HUP `ps -ef | grep signal_test_sigaction | grep -v grep | awk '{print $2}'`実行すると、

no      :[1]
si_signo:[1]
si_errno:[0]
si_code :[0]
si_pid  :[17033]   <- コマンドを実行したターミナルの pid,および uid
si_uid  :[1004]
ctx     :[0x7fbffff460]

 といった結果が私の環境では得られました、

 なお、sa_sigaction宛に詳細情報を送る事も可能です。その方法は次回説明します。

修正履歴

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

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー