読者です 読者をやめる 読者になる 読者になる

C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き

C CentOS

1つの親プロセスとたくさんの子プロセスという構成。親プロセスに SIGTERM を送ると、すべての子プロセスをきれいに終了させた後で終了するようにシグナルハンドラを追加したサンプル。

  • my_prefork_signal.c

シグナルハンドラ付きのソース

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

#define MAX_CHILDREN 16 //子プロセスの数

void kill_all_children(int);
void signal_handler(int);

// 子プロセスの管理にハッシュテーブルを使う
static apr_pool_t* pool;
static apr_hash_t* hChildren;

int main(void){

  // SIGTERM ですべての子プロセスを殺すようにシグナルハンドラを設定
  signal(SIGTERM, kill_all_children);

  // ハッシュの初期化
  apr_initialize();
  apr_pool_create(&pool, NULL);
  hChildren = apr_hash_make(pool);
  
  //親プロセスのループ
  while(1){
	while( apr_hash_count(hChildren) >= MAX_CHILDREN ){
	  int status;
	  pid_t child_pid = wait( &status ); //子プロセスが死ぬまで待つ
	  apr_hash_set(hChildren, &child_pid, sizeof(child_pid), NULL); //死んだ子プロセスをハッシュテーブルから削除
	}

	pid_t *pid = apr_palloc(pool, sizeof(pid_t));
	*pid = fork(); //フォーク
	if(*pid==0){
	  signal(SIGTERM, signal_handler);
	  goto CHILDREN; //子プロセスだったら、ループから抜ける
	}
	apr_hash_set(hChildren, pid, sizeof(pid_t), 1); //子プロセスをハッシュテーブルに追加
	usleep(100);
  }
  
 CHILDREN:
  while(1){
	//子プロセスの処理をここに書く
	sleep(1);
  }
}

void kill_all_children(int sig){
  apr_hash_index_t *hi;
  apr_ssize_t klen;
  pid_t *child_pid_ptr;
  int val;
  for( hi=apr_hash_first(pool, hChildren); hi; hi=apr_hash_next(hi) ){
	apr_hash_this(hi, &child_pid_ptr, &klen, &val);
	int ret = kill(*child_pid_ptr, SIGTERM);
  }
  exit(0);
}

void signal_handler(int sig){
  exit(0);
}

細かいところはいい加減なので、警告がやたらでるので本番で使うときは -Wall を付けて警告がなくなるまで要修正。

# gcc -g -I /usr/include/apr-1 -L/usr/lib/apr-1 -lapr-1 my_prefork_signal.c -o my_prefork_signal
  • 実行
$ ./my_prefork_signal &
[1] 8502
$ ps -ef|grep my_prefork_signal
503       8502  4614  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8503  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8504  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8505  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8506  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8507  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8508  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8509  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8510  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8511  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8512  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8513  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8514  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8515  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8516  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8517  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8518  8502  0 23:32 pts/3    00:00:00 ./my_prefork_signal
503       8521  4614  0 23:33 pts/3    00:00:00 grep my_prefork_signal
  • プロセスの終了方法

親プロセスに対してシグナルを送信すれば終了できる。
シグナルの送信方法は、

親プロセスのプロセスIDを調べる
# ps -ef

そのプロセスIDにSIGTERMを送る。
# kill 8502

参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle