理系学生日記

おまえはいつまで学生気分なのか

Signals

シグマルは、プロセスに対する特別なメッセージである。シグナルを受けたプロセスは、即座に(非同期に)そのシグナルを処理する。各シグナルにはデフォルトの処理があり、プログラムで明示的にシグナルハンドラを指定していない場合は、あるいはそのシグナルを無視する設定がされていない場合は、そのデフォルト処理が実行される。

プロセスは別のプロセスにシグナルを送ることもできる。代表的な例は、他のプロセスを終了させる SIGTERM や SIGKILL。あるいは、ユーザ定義の SIGUSR1、SIGUSR2 を用いて他のプロセスへのコマンドを送るといった使い方もされる。SIGHUP がこの役割をになうこともある。

シグナルハンドラを指定する関数が sigaction である。sigaction の最初のパラメータはシグナル番号であり、次の 2 つのパラメータは sigaction 構造体となる。sigaction 構造体の最も重要なパラメータは sa_handler であり、以下 3 つのいずれかの値を取る。

  • SIG_DFL: シグナルに対してデフォルトの動作を行う
  • SIG_IGN: シグナルを無視する
  • シグナルハンドラへのポインタ。シグナルハンドラは唯一の引数にシグナル番号を取り、戻り値はない(void)

シグナルハンドラは非同期に実行されることから、メインプログラムを非常にナイーブな状態で放置する場合がある。そのため、シグナルハンドラ内での I/O、システムコール呼び出し等は避けるべきである。また、できるだけ早くメインプログラムに復帰させるべきである。もしシグナルハンドラ実行中に、別のシグナルハンドラが呼び出されると、デバッグを極めて難しくする。例えば、シグナルハンドラ内でグローバル変数に値を設定しようとしたとき、その代入文が複数の機械語で実行される形になる場合を想定する。このとき、代入文を構成する複数の機械語の途中でシグナルが発生すると、代入文は破綻してしまう。
このような状態を避けるために代入文を atomic にしたいときは、sig_atomic_t を使う。sig_atmic_t への代入は atomic に実行されることが保証される。

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

sig_atomic_t sigusr1_count = 0;

void handler ( int signal_number ) {
  ++sigusr1_count;
}


int main() {
  struct sigaction sa;
  memset( &sa, 0, sizeof( sa ) );
  sa.sa_handler = &handler;
  sigaction (SIGUSR1, &sa, NULL);

  while( sigusr1_count < 2 ) {
  }

  printf( "SIGUSR1 was raised %d times\n", sigusr1_count );
  return 0;
}
$ gcc sigusr1.c
$ ./a.out &
[1] 11292
$ kill -SIGUSR1 11292
$ kill -SIGUSR1 11292
SIGUSR1 was raised 2 times