【sigaction】シグナルハンドラで非同期処理【SIGALRM】

| 0件のコメント

とあるマイナーHTTPサーバのプラグイン開発でRTSPサーバとの連携実装しようと思ったら、fdの遅延解放がどうしても必要なタイミングで適切なhook-entryが見つからなかったので、この非同期処理をシグナルを使って実装しました。

sigactionの実装は以下の通り。注意点として singal(2) ではなく sigaction(2) です。

struct sigaction {
	union {
	  __sighandler_t _sa_handler;
	  void (*_sa_sigaction)(int, struct siginfo *, void *);
	} _u;
	sigset_t sa_mask;
	unsigned long sa_flags;
	void (*sa_restorer)(void);
};

timer.it_interval.tv_secで設定した値の間隔でSIALRMを発生させます。
中ではalarm(2)システムコールを使っていて, timerが切れた時にシグナルがカーネルからプロセスに対して通知されSignalHandlerで操作を行えます。

int main(void)
{
  struct sigaction action;
  struct itimerval timer;
  memset(&action, 0, sizeof(action));

  /* set signal handler */
  action.sa_handler = SignalHandler;
  action.sa_flags = SA_RESTART;
  sigemptyset(&action.sa_mask);
  if(sigaction(SIGALRM, &action, NULL) < 0){
    perror("sigaction error");
    exit(1);
  }

  /* set timer */
  timer.it_value.tv_sec = 1;
  timer.it_value.tv_usec = 0;
  timer.it_interval.tv_sec = 1;
  timer.it_interval.tv_usec =0;
  if(setitimer(ITIMER_REAL, &timer, NULL) < 0){
    perror("setitimer error");
    exit(1);
  }

  printf("waiting timer for fire\n");

  for(;;){};
  return 0;
}

SignalHandlerでは任意の処理を行う事ができますが制約は多いです。こちらから抜粋すると以下があるそうです。

  • 非同期シグナル安全(async-signal-safe)な関数の呼び出し
  • volatile sig_atomic_t型変数の読み書き
  • シグナルハンドラ内の自動変数の読み書き

malloc(), free()などヒープ領域を使う関数は使用できません。アトミックでない操作はできないということです。
分かり易い例では、main()でfree()解放中に、偶然シグナルハンドラが発生すると二重解放となる可能性があります。

また, sig_atomic_t型変数の読み書きとありますが, 実際はtypedef int なのでなんとも。

typedef int sig_atomic_t; 

SignalHandlerではsignumは 14(SIGALRM) が入ってきます。close(), kill(), recvfrom()などは非同期安全関数なので使用できます。

void SignalHandler(int signum)
{
    close(fd);
    return;
}

Signalの種類がこんなにあるとは知らなかったです。

# centos 6.5 
$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

SIGCHLDは fork() 等で生成した子プロセスが終了したタイミングで親プロセスに通知されるシグナルです。

[1] Linuxカーネルでは/arch/arm/include/uapi/asm/signal.hや/arch/arm/include/asm/signal.hにARM実装があります。(github)
[2] ちなみにsignalの仕組みはLinux Kernel 0.01 からあります。

コメントを残す

必須欄は * がついています