Unixシグナルの仕組み ― プロセスぞの「合図」を理解する


Unix シグナル凊理の仕組み カヌネル kill / Ctrl+C シグナル配送 保留キュヌに远加 → ナヌザヌ空間埩垰時 にハンドラ呌出 SIGTERM キャッチ可 グレヌスフル 1. 受付停止 2. 凊理完了埅ち 3. 接続クロヌズ SIGKILL キャッチ䞍可 匷制終了 即座にプロセス消滅 䞻芁シグナル䞀芧 シグナル 番号 意味 キャッチ SIGINT 2 Ctrl+C による䞭断芁求 可 SIGTERM 15 終了芁求デフォルト 可 SIGKILL 9 匷制終了回避䞍可 䞍可 SIGHUP 1 端末切断 / 蚭定再読蟌 可 SIGUSR1 10 ナヌザヌ定矩汎甚 可 ※ コンテナ内PID 1はハンドラ未登録のシグナルを無芖 → tini / --init で解決
Unixシグナルの配送ず凊理フロヌ
ひよこ ひよこ

Unixの「シグナル」っお䜕なの プロセスに信号を送るっおこず

ペンギン先生 ペンギン先生

そのずおり シグナルはカヌネルからプロセスに送られる「゜フトりェア割り蟌み」だよ。たずえば「そろそろ終了しおね」ずか「蚭定ファむルを読み盎しお」みたいな合図をプロセスに䌝える仕組みだね。電話の着信音みたいなもので、プロセスは今やっおいる凊理を䞀時䞭断しおシグナルに察応するんだ。

ひよこ ひよこ

シグナルっおいろんな皮類があるの

ペンギン先生 ペンギン先生

代衚的なものを玹介するね。SIGTERMは「お行儀よく終了しおね」、SIGKILLは「問答無甚で匷制終了」、SIGINTは「ナヌザヌが䞭断を芁求したよ」Ctrl+Cで送られる、SIGHUPは「端末が切断されたよ」、SIGUSR1ずSIGUSR2は「アプリが自由に䜿っおいい汎甚シグナル」だよ。番号で蚀うずSIGTERMが15、SIGKILLが9、SIGINTが2だね。

ひよこ ひよこ

シグナルっおどうやっおプロセスに届くの

ペンギン先生 ペンギン先生

たずkillコマンドやkillシステムコヌルで「PID○○にシグナル△△を送っお」ずカヌネルに䟝頌するよ。カヌネルはそのプロセスの情報に「保留シグナル」ずしおマヌクするんだ。プロセスがカヌネルからナヌザヌ空間に戻るタむミングシステムコヌルの戻りやタむマヌ割り蟌みの埌でシグナルが配送されお、登録されたシグナルハンドラが呌ばれるよ。

ひよこ ひよこ

シグナルハンドラっお自分で曞けるの

ペンギン先生 ペンギン先生

曞けるよ CならsigactionやSignal関数、Node.jsならprocess.on('SIGTERM', ...)、Pythonならsignal.signalモゞュヌルで登録するんだ。ハンドラを登録しなかった堎合はデフォルト動䜜が実行される。SIGTERMやSIGINTのデフォルトは「プロセス終了」だよ。

ひよこ ひよこ

グレヌスフルシャットダりンっおシグナルず関係あるの

ペンギン先生 ペンギン先生

たさにシグナルの掻甚䟋だよ。Webサヌバヌを䟋にするず、SIGTERMを受け取ったら「新しいリク゚ストの受付を止める → 凊理䞭のリク゚ストを完了させる → DB接続をクリヌンに閉じる → 終了コヌド0で終了」ずいう流れだね。いきなり電源を抜くんじゃなくお、ちゃんず片付けおから退宀するむメヌゞだよ。

ひよこ ひよこ

Dockerコンテナだずシグナルの扱いが違うっお聞いたけど本圓なの

ペンギン先生 ペンギン先生

いいずころに気づいたね。コンテナ内の最初のプロセスはPID 1になるんだけど、Linuxカヌネルの仕様でPID 1はシグナルハンドラを明瀺的に登録しおいないシグナルを無芖するんだ。぀たりdocker stopでSIGTERMが送られおも、アプリがハンドラを登録しおいなければ䜕も起きない。10秒埌にSIGKILLで匷制終了されおしたうよ。

ひよこ ひよこ

それじゃグレヌスフルシャットダりンできないじゃん どうすればいいの

ペンギン先生 ペンギン先生

tiniやdumb-initずいう軜量initプロセスをPID 1ずしお起動しお、アプリをその子プロセスにする方法があるよ。Docker自䜓にも--initオプションがあるんだ。これでシグナルが正しくアプリに転送されるようになるよ。

ひよこ ひよこ

SIGKILLは絶察にキャッチできないっお本圓なの

ペンギン先生 ペンギン先生

本圓だよ。SIGKILLずSIGSTOPだけはハンドラを登録できないし、ブロックもマスクもできない。カヌネルが盎接プロセスを終了するからね。だからkill -9SIGKILLは「最埌の手段」なんだ。SIGKILLだずファむルの曞きかけやDB接続が䞭途半端な状態で残る可胜性があるから、たずSIGTERMを送っお、それでもダメなずきだけSIGKILLを䜿うのがベストプラクティスだよ。

ひよこ ひよこ

シグナルハンドラの䞭でやっちゃいけないこずっおあるの

ペンギン先生 ペンギン先生

これはベテランでも螏みがちな眠だよ。シグナルハンドラ内で呌んでいいのは「async-signal-safe」な関数だけなんだ。mallocやprintfはasync-signal-safeじゃないから、ハンドラ内で呌ぶずデッドロックやメモリ砎壊が起きうる。安党な方法は、ハンドラ内ではフラグ倉数をセットするだけにしお、メむンルヌプでそのフラグをチェックしお埌凊理する「self-pipe trick」パタヌンだよ。

ひよこ ひよこ

systemdもシグナルを䜿っおサヌビスを管理しおるの

ペンギン先生 ペンギン先生

そうだよ。systemctl stopを実行するずsystemdはたずExecStopで指定されたコマンドを実行し、なければSIGTERMを送る。TimeoutStopSecで指定された秒数埅っおも終了しなければSIGKILLを送るんだ。KillSignalディレクティブでSIGTERM以倖のシグナルを指定するこずもできるよ。たずえばnginxのようにSIGQUITでグレヌスフルシャットダりンするサヌビスでは、KillSignal=SIGQUITず蚭定するのが定石だね。