sigaction

sigaction関数はsignal関数よりもより細かい制御ができる.sigactionはX/Open仕様ではsignalよりも信頼できるとして推奨しているらしい.
sigaction構造体
サンプル
sigset_t
sigactionの仲間達
サンプル2
sigactionのメモ

sigaction構造体

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigactionの引数について以下に示す.
signum
sigaction構造体を設定するシグナル.
act
設定するsigaction構造体.
oldact
設定前(変更前)のsigaction構造体.
sigaction関数は成功すれば0,失敗すると-1を返す.sigaction関数ではsigaction構造体を引数にとる.この構造体がsignal関数との違いの鍵を握っている.以下,/usr/include/bits/sigaction.hより

/* sigaction��¤�� */
struct sigaction
{
#ifdef __USE_POSIX199309
  union
  {
    /* signal�ؿ��ΰ���������֤��ͤ�Ʊ��
       sa_flags��SA_SIGINFO �����ꤵ��Ƥ��ʤ����˻Ȥ�*/
    __sighandler_t sa_handler;
    /* sigaction�ؿ�
       sa_flags��SA_SIGINFO �����ꤵ��Ƥ�����˻Ȥ�*/
    void (*sa_sigaction) (int, siginfo_t *, void *);
  }
  __sigaction_handler;
# define sa_handler     __sigaction_handler.sa_handler
# define sa_sigaction   __sigaction_handler.sa_sigaction
#else
  __sighandler_t sa_handler;
#endif
  /* �����ʥ�ϥ�ɥ�¹���˥֥��å����륷���ʥ�Υޥ��� */
  __sigset_t sa_mask;

  /* �����ʥ�ϥ�ɥ��ư��������뤿��Υե饰 */
  int sa_flags;

  /* �ѻ�.���Ѥ��ƤϤʤ�ʤ�. */
  void (*sa_restorer) (void);
};

sa_sigactionは3つの引数を取る関数で,そのうちの一つsiginfo_tは構造体になっていて,またたくさんのフィールドを持つ.面倒なのでここでは書かないが,こいつを使うとさらに細かい制御ができそうな雰囲気を感じた.

サンプル

sigactionを使ったサンプルを以下に示す.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

/* �����ʥ�ϥ�ɥ� */
void sig_handler_SIGINT(int sig);

int main(void)
{
  /* sigaction��¤�Τ���� */
  struct sigaction act_SIGINT;

  /* �����ʥ뽸��ν��� */
  sigset_t *sigset = NULL;
  sigset = malloc(_SIGSET_NWORDS);

  errno = 0;
  if(sigset == NULL){
    perror("failed to malloc");
    exit(EXIT_FAILURE);
  }
  errno = 0;
  if(sigemptyset(sigset)){
    perror("failed to sigemptyset");
  }
  errno = 0;
  if(sigaddset(sigset,SIGUSR1)){
    perror("failed to sigaddset");
  }
  /* �����ʥ뽸��ν��� ����*/

  /* sigaction���� */
  act_SIGINT.sa_handler = sig_handler_SIGINT;
  act_SIGINT.sa_mask = *sigset;
    /* �ϥ�ɥ������ϰ�����ͭ�� */
  act_SIGINT.sa_flags = SA_RESETHAND;

  /* �����ʥ�ϥ�ɥ������ */
  errno = 0;
  if(sigaction(SIGINT,&act_SIGINT,NULL)){
    perror("failed to sigaction");
  }else{
    fprintf(stdout,"successed sigaction.\n");
  }

  /* �����ʥ��Ф��Ƥߤ� */
  {
    int i;
    
    for(i = 0;i<3;i++){
      errno = 0;
      if(raise(SIGINT)){
	perror("failed to raise");
      }else{
	fprintf(stdout,"successed raise.\n");
      }
    }
  }

  exit(EXIT_SUCCESS);

}

/* �����ʥ�ϥ�ɥ� */
void sig_handler_SIGINT(int sig){
  fprintf(stdout,"catched SIGINT.\n");
}
以下実行結果.
successed sigaction.
catched SIGINT.
successed raise.

sa_gflagsをSA_ONESHOTにした効果(一回シグナルをキャッチしたらデフォルトのsigactionに戻る)もちゃんと出ている.

sigset_t

sigaction構造体のフィールドsa_maskはsigset_t型でシグナルの集合を表すものある.これはつきつめると/usr/include/bits/sigset.hの以下の部分になっているようだ.
/* A `sigset_t' has a bit for each signal.  */

# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
  {
    unsigned long int __val[_SIGSET_NWORDS];
  } __sigset_t;

#endif
で先のサンプルでは思い切って
sigset = malloc(_SIGSET_NWORDS);
などとしてしまった(いいのかな).で,このシグナル集合を扱うのに便利な関数がある.
int sigemptyset(sigset_t *set);
setを空に初期化.
int sigfillset(sigset_t *set);
setにすべてのシグナルを設定.
int sigaddset(sigset_t *set, int signum);
setに指定したシグナルを追加.
int sigdelset(sigset_t *set, int signum);
setから指定したシグナルを削除.
int sigismember(const sigset_t *set, int signum);
setに指定いたシグナルが含まれていれば1,含まれていなければ0,signumが不正などの理由でエラーなら-1を返す.
前四者は成功なら0,失敗なら-1を返す.で,このうちsigemptyset,sigaddsetを先のサンプルで使用した(/* シグナル集合の準備 */の部分).

sigactionの仲間達

仲間かどうかは知らないが,とにかくman sigactionとすると一緒に出て来る関数が三つある.
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
ブロックされているシグナルのリストを変更する.howで追加,設定,削除を選択する.man参照.
int sigpending(sigset_t *set);
(ブロックされていて)待機中のシグナルのセットを引数setに格納する.
int sigsuspend(const sigset_t *mask);
シグナルのマスクを一時的にmaskに置き換えてシグナルを受信するまで待機する.返り値は常に-1.
前者二つはsigactionと同様成功なら0,失敗なら-1を返す.

サンプル2

sigpendingやsigismemberを使用したサンプルを示す.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

/* �����ʥ�ϥ�ɥ� */
void sig_handler_SIGINT(int sig);
/* �����ʥ뽸�� */
sigset_t *sigset = NULL;

int main(void)
{
  /* sigaction��¤�Τ���� */
  struct sigaction act_SIGINT;

  /* �����ʥ뽸��ν��� */
  sigset = malloc(_SIGSET_NWORDS);

  errno = 0;
  if(sigset == NULL){
    perror("failed to malloc");
    exit(EXIT_FAILURE);
  }
  errno = 0;
  if(sigemptyset(sigset)){
    perror("failed to sigemptyset");
  }
  errno = 0;
  if(sigaddset(sigset,SIGUSR1)){
    perror("failed to sigaddset");
  }

  /* sigaction���� */
  act_SIGINT.sa_handler = sig_handler_SIGINT;
  act_SIGINT.sa_mask = *sigset;
  act_SIGINT.sa_flags = 0;

  /* �����ʥ�ϥ�ɥ������ */
  errno = 0;
  if(sigaction(SIGINT,&act_SIGINT,NULL)){
    perror("failed to sigaction");
  }else{
    fprintf(stdout,"successed sigaction.\n");
  }

  fprintf(stdout,"type C-c\n");

  /* �Ԥ�³���� */
  while(1){
    sleep(100);
  }

  exit(EXIT_SUCCESS);

}

/* �����ʥ�ϥ�ɥ� */
void sig_handler_SIGINT(int sig){
  /* ���Υ����ʥ�ϥ�ɥ餬�ƤФ줿��� */
  static int count = 1;
  fprintf(stdout,"signal handler called %d times.\n",count++);

  /* C-c�򤬤󤬤��Ǥ����� */
  fprintf(stdout,"type C-c more\n");
  sleep(3);

  /* �֥��å�����Ƥޤ�����Ƥ��륷���ʥ������å� */
  errno = 0;
  if(sigpending(sigset)){
    perror("failed to sigpending");
    exit(EXIT_FAILURE);
  }

  errno = 0;
  switch(sigismember(sigset,SIGINT)){
  case 1:
    fprintf(stdout,"SIGINT is pending\n");
    break;
  case 0:
    fprintf(stdout,"SIGINT is not pending\n");
    break;
  case -1:
  default:
    perror("failed to sigismember");
    exit(EXIT_FAILURE);
    break;
  }

  fprintf(stdout,"signal handler done.\n");
}
以下実行結果.C-\でSIGQUITを送信して終了できる.
successed sigaction.
type C-c
signal handler called 1 times.
type C-c more
SIGINT is pending
signal handler done.
signal handler called 2 times.
type C-c more
SIGINT is not pending
signal handler done.
終了
このサンプルから,シグナルハンドラ実行中にブロックされている同じシグナルを何回送信しても一回しかシグナルハンドラが起動されないことが分かる.具体的には
type C-c more
と出てから3秒以内に複数回C-cをタイプして放置しても,一回しかシグナルハンドラが起動されていないことが分かる.
sigactionのメモ
man sigactionで地味に書いてあったのをメモ.
sigactionの二番目の引数をNULLにして呼び出し,そのあとoldactを参照すれば,現在のハンドラを確認できる.
二番目と三番目の引数をNULLにして呼び出すことで,signumが現在のマシンで有効かどうかを確認できる.

This article was written by Fujiko