SIGTERMの受信設定と動作例 - Apache httpd デーモンの場合 -
次に、Linuxディストリビューションで提供されている代表的なプログラムにおけるシグナルハンドラの実装例を見てみましょう。まずは主要なLinuxディストリビューションで提供されているApache Webサーバのpreforkモデルについて見ていきます。
preforkモデルでは、親プロセス(httpd)が子プロセスを先行して生成し、1つの子プロセスが1つのリクエストを処理します。
シグナル処理はhttpdの本体からリンクされたモジュールmod_mpm_prefork.soによって行われます。この中で、親プロセスはシグナルハンドラsig_term()
の登録を行い、SIGTERM
シグナル受信時にクリーンアップ処理を行います。親プロセスのシグナルハンドラの登録にはsigacton()
を、子プロセスのシグナルハンドラの登録にはsignal()
を使用しています。
図「httpdのSIGTERMシグナルハンドラ」の①~③で実行されている動作
-
① 親プロセスはシステムコール
sigaction()
によりシグナルハンドラsig_term()
を登録(子プロセスはシステムコールsignal()
によりシグナルハンドラjust_die()
を登録) -
②
kill
コマンドによりhttpdの親プロセスにSIGTERM
を送信 -
③
SIGTERM
シグナルを受信したhttpd(親)のシグナルハンドラsig_term()
は終了待ちフラッグを立て、それを検知した処理ルーチンはSIGTERM
シグナルの送信によって、すべての子プロセスを終了させ、最後に親プロセス自身が終了。
このように、httpdはSIGTERM
シグナルを受信するとクリーンアップ処理をしてから終了します。
ただし、httpdの親プロセスをSIGKILL
シグナルによって強制終了した場合、クリーンアップ処理が行われず、子プロセスが残ってしまいます[1]。httpdの親プロセスが消滅した場合は、子プロセスに対して設定ファイルに従った正しい制御ができず、またSysVinitのservice
コマンドやsystemdのsystemctl
コマンドでも制御ができなくなります[2]。
SIGTERMとSIGKILLの違い
httpdの例で見てきたように、SIGTERM
シグナルを受信したプロセスはクリーンアップ処理を実行して終了し、SIGKILL
シグナルを受信したプロセスはシグナルを捕捉できないためクリーンアップ処理を実行できずに終了します。WebブラウザのFirefoxやメールクライアントのThunderbird(共にLinux版)では、終了時のクリーンアップ処理の中でロックファイル(lock)を削除して終了しますが、SIGKILL
シグナルを送信して終了させた場合には、ロックファイルは削除されずに残ってしまいます。このため、次回起動時にはロックファイルを削除してからでないと起動できません。
このように、外部からシグナルを送信してプロセスを終了させる場合には、プロセスがクリーンアップ処理を実行することのできないSIGKILL
シグナルによる強制終了ではなく、プロセスがクリーンアップ処理を実行するSIGTERM
シグナルによる終了が推奨されます[3]。
SIGHUPの受信設定と動作例 - BIND named デーモンの場合 -
BIND named、Apache httpd、xinetdなどのデーモンプログラムは、SIGHUP
シグナルを受信すると設定ファイルを再読み込みするように実装されています。このようなデーモンは、設定ファイルを編集した後にSIGHUP
シグナルを送信することで、再起動によるサービスの中断なしに設定ファイルの内容を有効にすることができます[4][5]。
ここではDNSサーバ「BIND named」(以下、named)のシグナル処理を例として見ていきます。namedはsigacton()
を使用してシグナルハンドラreload_action()
を登録し、SIGHUP
シグナル受信時に設定ファイルの再読み込みを行います。
-
➀ システムコール
sigaction()
により、シグナルハンドラreload_action()
を登録 -
②
kill
コマンドによりnamedにSIGHUP
シグナルを送信 -
③
SIGHUP
シグナルを受信したnamedのシグナルハンドラreload_action()
は設定ファイルnamed.confとゾーンファイルの再読み込みを行う
注
[1]: 親プロセスが子プロセスの終了処理をせずに強制終了で消滅した場合、SysVinitを採用しているシステムではinitプロセスが、systemdを採用しているシステムではsystemdが代わって親になります。
[2]: この場合、killall
コマンドやpkill
コマンドなどで、すべての子プロセスを終了させる必要があります。
[3]: SIGKILL
シグナルは、設定あるいは動作異常によりSIGTERM
シグナルの処理を行わないプロセスを強制終了する場合に使用します。
[4]: SIGHUP
シグナルを受信すると再起動するデーモンもあります。また、デフォルトのハンドラが起動してプロセスが終了するデーモンもあります。
[5]: Apache httpdの場合、SIGHUP
シグナルを受信した親プロセスは設定ファイルを再読み込みし、子プロセスを再起動します。