Signal 是什麼#
Signal 是 Linux 提供給 process 的「軟體中斷」。當某個事件發生(使用者按 Ctrl-C、子行程結束、kernel 偵測 segfault),kernel 會向 process 發送一個 signal,打斷它原本的執行流程,跳去執行對應的 signal handler。
- Signal 是非同步的:可能在任何指令之間插入
- Signal 是有編號的:例如 SIGTERM=15、SIGKILL=9
- Signal 有「disposition」:default、ignore、custom handler
常用 Signal 一覽#
下列幾個 signal 在 container 與服務管理場景最常用,值得熟記。
- SIGTERM(15):請求終止,可被捕捉、可被忽略,預設行為是終止
- SIGKILL(9):強制終止,不可被捕捉、不可被忽略
- SIGINT(2):使用者中斷(Ctrl-C),可被捕捉,預設終止
- SIGHUP(1):終端斷線;服務常用作 reload 設定的訊號
- SIGCHLD(17 在 x86_64):子行程狀態改變(exit、stopped、continued)
- SIGUSR1(10)/SIGUSR2(12):使用者自訂語意
- SIGSTOP(19):暫停 process,不可被捕捉
- SIGCONT(18):恢復暫停的 process
SIGKILL 與 SIGSTOP 是 kernel 強制執行的,無法被 process 自己改變行為。其他幾乎都可以捕捉或忽略。
Signal Disposition#
每個 signal 對某個 process 有一種「disposition」設定,描述收到時要做什麼。
- Default(SIG_DFL):依 signal 種類有預設行為(terminate、ignore、core dump、stop、continue)
- Ignore(SIG_IGN):直接丟掉,不做任何事
- Custom handler:跳到使用者指定的函式執行
切換 disposition 用 sigaction(2)(推薦)或 signal(2)(古老、語意不一致)。
Trap:Shell 層級的 Signal Handler#
在 shell script 裡,要對 signal 做事不是用 sigaction,而是用 trap。
#!/bin/bash
trap 'echo got SIGTERM; exit 0' TERM
trap 'echo got SIGINT; exit 130' INT
while true; do
sleep 1
donetrap '<command>' SIGNAME ...:收到該 signal 時執行 commandtrap '' SIGNAME:忽略trap - SIGNAME:恢復預設
trap在 bash 與 dash 行為類似,但 dash(Ubuntu /bin/sh)不支援某些 bash 擴充,後章會展開。
哪些 Signal 可以忽略#
絕大多數 signal 都可以忽略(SIG_IGN),但有兩個例外:
- SIGKILL:永遠送達、永遠終止
- SIGSTOP:永遠送達、永遠暫停
這兩者無法 catch、無法 ignore、無法 block。它們是 kernel 給 root 的最後手段。
Signal 與 Process Group#
Linux signal 可以一次送給整個 process group(PGID)。kill -TERM -PGID(PID 給負數)會把 SIGTERM 廣播給 group 內所有成員。
- Shell job control 依賴 process group
- Container init(tini)也用這個機制把 signal 從 PID 1 廣播下去
- 若應用是 process group leader,killpg(2) 一次就能搞定整個子樹
Signal 與 PID Namespace#
Container 內的 PID namespace 對 signal 有兩個影響。
- 從 host 對 container PID 1 發 signal,需要用 host 視角的 PID
- Container 內
kill -TERM 1因為 kernel 對 PID 1 的特殊保護,往往不會生效(除非 PID 1 自己裝過 handler)
詳細的 PID 1 保護規則見最後一章
11-can-pid1-be-killed。
一張行為對照表#
| Signal | 編號 | 預設 | 可 catch | 可 ignore | 典型用途 |
|---|---|---|---|---|---|
| SIGTERM | 15 | terminate | 是 | 是 | graceful shutdown |
| SIGKILL | 9 | terminate | 否 | 否 | 強制終止 |
| SIGINT | 2 | terminate | 是 | 是 | Ctrl-C |
| SIGHUP | 1 | terminate | 是 | 是 | reload / 斷線 |
| SIGCHLD | - | ignore | 是 | 是 | reap |
| SIGUSR1 | - | terminate | 是 | 是 | 自訂 |
| SIGSTOP | 19 | stop | 否 | 否 | 強制暫停 |
延伸閱讀#
- man 7 signal
- man 2 sigaction
- man 1 trap (built-in in bash/dash)