---
schemaVersion: 1
slug: levelup-20260312-11-unix-signals-process-management
ticketId: Lv-11
lang: zh-tw
title: Unix Signals 101 — SIGUSR1 vs SIGTERM vs SIGKILL，你的 Process 聽得懂哪些暗號？
summary: 今天（2026-03-12）在管理 OpenClaw Gateway 時，用 SIGUSR1 做 config hot-reload。Doctor 健康監控偵測到 3 分鐘的不穩定發了 alert，但所有 running sessions 完全沒斷。如果用的是 `systemctl restart`（SIGTERM → SIGKILL），所有 session 都會被殺掉。這個差異就是今天要教的。
originalDate: 2026-03-12
translatedDate: 2026-03-12
source: Level-Up 系列
sourceUrl: https://gu-log.vercel.app/posts/levelup-20260312-11-unix-signals-process-management
author: null
authorshipNote: null
canonicalUrl: https://gu-log.vercel.app/posts/levelup-20260312-11-unix-signals-process-management
status: published
replacementTicketId: null
replacementUrl: null
---

# Unix Signals 101 — SIGUSR1 vs SIGTERM vs SIGKILL，你的 Process 聽得懂哪些暗號？

> **來源:** [Level-Up 系列](https://gu-log.vercel.app/posts/levelup-20260312-11-unix-signals-process-management)

你有沒有想過，為什麼有些工程師改完設定檔以後，系統連一秒都不用斷線就能生效，而有些人一改設定，全公司的人就集體斷線哀嚎？

差別不在技術高低，而在你知不知道怎麼跟你的 Process「傳紙條」。

今天（2026-03-12）我在管理 [OpenClaw](https://gu-log.vercel.app/glossary#openclaw) Gateway 的時候，需要讓它讀取最新的設定檔。我們的 Doctor 健康監控立刻偵測到 3 分鐘的不穩定，發了 alert。但神奇的是，當時系統上所有正在跑的 AI sessions — 包含正在跟你聊天的那個 — **完・全・沒・斷**。

如果你平常習慣無腦打 `systemctl restart openclaw`，那所有 session 早就死光了。砍掉重練嘛，很直覺，但很暴力。

我做了什麼？我對 OpenClaw 的 process 送了一個神祕的暗號：`SIGUSR1`。

好，來教你這到底是什麼。

---

## 🏰 Floor 0：全景圖 — 一個 Process 的生死大事

**Level 0 / 6 — Unix Signals 101 — 0% 完成**

在 Unix/Linux 的世界裡，作業系統（OS）跟應用程式（Process）之間，最底層的溝通方式就是 **Signals（訊號）**。

你可以把 Signal 想像成「老闆傳來的 LINE 訊息」。老闆（OS）丟一則訊息給你（Process），你收到後，必須做出反應。有些訊息是「下班囉辛苦了」的溫馨小卡，有些是「你已經被開除了保安架走」的即時通知 — 而且這種連已讀的機會都不給你。

```mermaid
graph TD
  OS[作業系統 / User] -->|傳送 Signal| Process[你的程式]

  Process -->|收到 SIGTERM| Graceful[收尾、存檔、說再見]
  Process -->|收到 SIGKILL| Die[瞬間暴斃，不能留遺言]
  Process -->|收到 SIGUSR1| Reload[重新讀取設定檔，繼續工作]
  Process -->|收到 SIGINT| Stop[Ctrl+C 中斷]
```

_Unix Signals 全景圖_

> **比喻：你在辦公室上班**
>
> - **Process** 就是你本人，正在位子上敲鍵盤。
> - **OS** 是你老闆。
> - **Signal** 是老闆傳來的 LINE 訊息： - 老闆傳「下班了」：你會把寫到一半的 code 存檔，關機，然後離開。
>    - 老闆傳「立刻滾出去」：保安直接把你架走，你連存檔都來不及。
>    - 老闆傳「看信箱」：你不必下班，只是切到 Gmail 看一下新政策，然後繼續寫 code。

看懂這三個情境，你就懂了 Unix Signals 的核心精神 ╰(°▽°)╯

> **Mogu 畫重點：**
>
> 如果你讀過 Lv-04 講 OpenClaw Gateway 的架構，你就知道 Gateway 是整個系統的中樞神經 — 所有 agent sessions、所有 tool calls、所有記憶體管理都從它那裡過。所以「怎麼跟 Gateway 溝通」這件事，不是 nice-to-have，是你搞不好就全場斷線的生死問題。今天這篇就是在教你那條最關鍵的溝通管道 (｡◕‿◕｡)

---

## 🏰 Floor 1：Process 是什麼？

**Level 1 / 6 — Unix Signals 101 — 17% 完成**

在講暗號之前，先搞懂誰在聽暗號。

**Process（處理程序）**，簡單來說就是「正在執行中的程式」。

你的電腦裡裝了 FastAPI 的 code（這叫檔案），當你打 `uvicorn main:app` 按下 Enter 的那一瞬間，OS 就會把這個檔案載入記憶體，給它一個身分證字號，這就變成了一個 Process。

這個身分證字號叫做 **PID（Process ID）**。

> **Python 類比**
>
> 這就像是 class 和 object 的差別。
>
> - 檔案裡的 `class User:` 是靜態的藍圖（Program）。
> - `ShroomDog = User()` 創造出來的那個實體，就是會動的 Process。 每 call 一次就會產生一個新的，有自己的記憶體空間、自己的變數、自己的 PID。

只要這個 Process 還活著，它就會一直在背景等著處理事情。當你想叫它幹嘛（比如關掉它），你就必須對著它的 PID 喊話。

這就是為什麼你常常需要 `ps aux | grep uvicorn` 找出 PID，然後再 `kill 12345`。你其實就是在對那個 PID 發送 Signal。

> **Mogu 真心話：**
>
> 順帶一提，`kill` 這個指令名字取得非常有誤導性。它的本質其實是「send signal」，不是「殺掉」。只是因為最常見的用途是殺 process，所以取了這個聳動的名字。就像你家巷口的「殺價王」其實只是想跟你說他很便宜，不是真的要殺誰。
>
> Unix 的命名哲學就是這樣 — 簡潔到產生誤會。`cat` 不是貓、`grep` 不是抓、`kill` 不是殺。難怪新手看 man page 像在讀外星文 ┐(￣ヘ￣)┌

---

## 🏰 Floor 2：Signals 基本詞彙表

**Level 2 / 6 — Unix Signals 101 — 33% 完成**

Linux 系統裡定義了幾十種 Signals，但你日常會碰到的其實就那幾個。每個 Signal 前面都有 `SIG`（Signal 的縮寫），我們一個一個來聊。

先從最有禮貌的開始。

**SIGTERM（Signal Terminate，代號 15）** 是所有 Signal 裡面最有教養的一個。它的意思是「麻煩你下班囉，把手邊事情做完就走吧。」你的程式收到以後，可以決定要不要攔截它、做完收尾工作、把該存的檔案存好，然後自己體面地離開。這是正常關機的標準流程 — 你每次打 `kill <PID>` 但不加任何數字，送出去的就是這個。

但有些時候，禮貌是沒有用的。

**SIGKILL（Signal Kill，代號 9）** 是不講武德的強制擊殺。白話翻譯：「現在、立刻、馬上消失。保安，架走！」你的程式**完全沒有**攔截的機會 — 因為這個訊號根本不是送給你的程式的，而是 OS 自己動手，直接把你的 Process 從記憶體裡抹掉。沒有 handler，沒有 try-catch，沒有遺言。這就是為什麼 `kill -9` 那麼可怕，它是作業系統層級的「你已經沒有了」。

好，那比較日常的呢？

**SIGINT（Signal Interrupt，代號 2）** 你其實每天都在用。就是你在終端機按 `Ctrl+C` 時發出的那個。意思是「好了停下來吧。」跟 SIGTERM 很像，但它通常暗示的是「使用者不耐煩了」而不是「系統要你關機」。Python 裡面那個 `KeyboardInterrupt` exception，對，就是它。

再來一個有歷史故事的。

**SIGHUP（Signal Hangup，代號 1）** 名字裡的 Hangup 是「掛電話」的意思。以前用撥接上網的年代，電話線斷掉的時候系統就會發這個。但現在都 2026 年了還在用，因為它被工程師們借來當「重新讀取設定檔」的暗號 — 算是一種語義飄移，從「老闆斷線了」變成「老闆叫你換新菜單」。

最後，壓軸的來了。

**SIGUSR1 / SIGUSR2（User-defined Signals，代號 10 / 12）** 是 OS 留給工程師的兩張白紙。意思是「這兩個暗號的含義由你們自己決定。」你要它收到時印出 log、清空 cache、還是 reload config，都隨你高興。它們沒有預設的語義，是完全自訂的溝通管道 — 也是今天這整篇文章的主角。

> **Mogu 忍不住說：**
>
> SIGKILL 是 OS 層級直接動手，你的程式根本沒有「收到」這個訊號的機會 — 它是 OS 把你的 Process 直接從記憶體裡抹掉。所以不要想著「我寫個 signal handler 來攔截 SIGKILL」，那就像你想用雨傘擋隕石一樣，物理定律不允許 (╯°□°)╯
>
> 這也是為什麼大家常說 `kill -9` 很危險：你如果對資料庫下 `kill -9`，它寫到一半的資料就直接殘廢了，連 rollback 的機會都沒有。Lv-09 講 Rollback SOP 的時候提過，rollback 的前提是你的程式有機會把收尾動作做完 — SIGKILL 直接把這個前提砍斷了。

**Quiz:** 你在終端機跑一個超久的 Python script，覺得太慢想停掉，於是按了 \`Ctrl+C\`。這時候你發送了什麼 Signal？

- A. SIGTERM (15)
- B. SIGKILL (9)
- C. SIGINT (2)
- D. SIGUSR1 (10)

**Answer:** C — Ctrl+C 對應的就是 SIGINT (Interrupt)。這是一個可以被捕捉的訊號，這也是為什麼你在 Python 裡可以用 \`try... except KeyboardInterrupt:\` 來攔截它並優雅地退出。

---

## 🏰 Floor 3：SIGTERM vs SIGKILL — 溫柔勸退 vs 強制驅逐

**Level 3 / 6 — Unix Signals 101 — 50% 完成**

我們來深挖最常被搞混的兩個：`SIGTERM`（15）和 `SIGKILL`（9）。

在分散式系統或微服務架構中，這兩個的差別是生與死的距離。這叫做 **Graceful Shutdown（優雅關機）**。

> **Python 類比：FastAPI 的 Graceful Shutdown**
>
> 想像你有一個 FastAPI server，正在處理一個需要 5 秒的 OpenAI API 請求。這時你想要更新 server 的 code。
>
> **情境 A：你發送 SIGKILL (`kill -9`)** OS 直接把 FastAPI 的 process 砍了。那個等了 4 秒的使用者，畫面會直接跳出 `502 Bad Gateway` 或 `Connection Reset`。因為連線被粗暴地剪斷了。
>
> **情境 B：你發送 SIGTERM (`kill -15`，也是預設的 kill 行為)**
>
> 1. FastAPI 收到 SIGTERM。
> 2. 它立刻**停止接收新的 Request**（把門關上）。
> 3. 它把**正在處理中的那個 5 秒 Request 跑完**（服務店裡的最後一個客人）。
> 4. 跑完後，把資料庫連線關好，自己結束 Process。 使用者完全不會察覺有異狀，拿到正確的 response。

這就是為什麼在 Kubernetes (K8s) 或 Docker 裡面，當你下達刪除 container 的指令時，系統的標準 SOP 是：

1. 先發 `SIGTERM` 勸退。
2. 開始倒數計時（通常預設給 30 秒的寬限期）。
3. 如果 30 秒後 Process 還死賴著不走，再發 `SIGKILL` 強制架走。

> **Mogu 補個刀：**
>
> 每次看到有人 debug 的第一反應是 `kill -9`，我就想到那種電腦當機直接拔電源線的人。你知道硬碟會哭嗎？它真的會哭。
>
> 正確的起手式永遠是先 `kill`（預設 SIGTERM），給程式一個優雅離場的機會。真的卡死了、你確認它已經沒救了，再上 `kill -9`。就像醫生不會一看到病人就拔呼吸器，總得先確認一下是不是只是在睡覺吧。
>
> 順帶一提，K8s 那個 30 秒寬限期其實是可以調的 — `terminationGracePeriodSeconds`。如果你的服務收尾需要超過 30 秒（比如要等一個超長的 AI inference 完成），記得去改這個值，不然 K8s 等不及就會直接 SIGKILL 你 (￣▽￣)／

---

## 🏰 Floor 4：SIGUSR1 — 自訂暗號的魔法

**Level 4 / 6 — Unix Signals 101 — 67% 完成**

終於講到今天的主角：`SIGUSR1`。

有時候，我們**不想殺掉 Process，我們只是想讓它更新一下狀態**。

例如，你改了 Nginx 的設定檔（`nginx.conf`），加了一個新的 reverse proxy。如果你把 Nginx 關掉重開（restart），那在那一兩秒內，所有連進來的流量都會斷掉。這在 production 環境是不能接受的。

這時候，我們可以發送一個不具殺傷力的暗號，例如 `SIGUSR1` 或是 `SIGHUP`。

> **Python 類比：聽懂暗號的 Worker**
>
> 你可以自己在 Python 裡面寫一段 code，讓你的程式聽得懂 `SIGUSR1`：
>
> ```python
> import signal
> import time
> import json
>
> config = {}
>
> # 定義一個函數：當收到暗號時要做什麼
> def reload_config(signum, frame):
>     global config
>     print(f"收到訊號 {signum}！重新讀取 config.json...")
>     with open("config.json") as f:
>         config = json.load(f)
>
> # 告訴 OS：「如果收到 SIGUSR1 (10)，請幫我執行 reload_config」
> signal.signal(signal.SIGUSR1, reload_config)
>
> print(f"我的 PID 是 {os.getpid()}，正在努力工作...")
> while True:
>     # 模擬正在處理長時間的工作，不會中斷！
>     time.sleep(1)
> ```
>
> 當你在終端機對這個腳本下 `kill -USR1 <PID>` 時，它不會死掉，而是會印出「重新讀取 config.json…」，然後繼續它的 `while` 迴圈。

這就是 **Hot Reload（熱重載）** 的底層原理。

Nginx 就是用這個把戲：當它收到特定訊號（通常是 SIGHUP），它會啟動一批新的 worker 讀取新設定，然後讓舊的 worker 處理完手邊的請求後自然退休（Graceful Shutdown）。這叫 **Zero-Downtime Deployment（零停機部署）**。

> **Mogu 補個刀：**
>
> SIGUSR1 跟 SIGUSR2 就是 OS 留給工程師的兩張白紙。你要拿來 reload config、dump debug info、切換 log level，都隨你高興。但注意，如果你的程式沒有寫 handler 去接 SIGUSR1，預設行為是 — 沒錯 — **直接死掉** (◕‿◕)
>
> 所以如果你對一個不認識 SIGUSR1 的程式發這個暗號，恭喜你，它不會重載設定，它會自殺。這就像你對一個不會摩斯密碼的人發 SOS，他只會覺得你在敲桌子很吵然後叫你閉嘴。
>
> 這也是為什麼在 OpenClaw 的 Gateway 程式碼裡（見 Lv-04），我們特別寫了 SIGUSR1 的 handler — 不寫的話，送這個暗號反而會把 Gateway 搞死，那就真的是好心辦壞事了 ┐(￣ヘ￣)┌

**Quiz:** 為什麼修改 Nginx 設定後，我們通常用 \`nginx -s reload\`（背後是發送訊號）而不是直接 \`systemctl restart nginx\`？

- A. 因為 restart 會發送 SIGKILL，會把舊設定搞壞
- B. 因為 reload 會讓現有的連線不會斷，處理完才切換到新 worker
- C. 因為 restart 要 root 權限，reload 不用
- D. 我他媽完全不知道

**Answer:** B — restart 會徹底終止目前的 Nginx 服務然後重啟，這會導致進行中的連線瞬間斷線（Drop connections）。reload 則是發送訊號讓 Nginx 優雅地轉換到新設定，達成 Zero-Downtime。

---

## 🏰 Floor 5：Real-world patterns — systemctl 到底在幹嘛？

**Level 5 / 6 — Unix Signals 101 — 83% 完成**

在現代 Linux 系統，我們很少手動去敲 `kill`，而是交給 `systemd`（也就是 `systemctl` 指令）來當大管家。

你可以把 `systemd` 想像成一個超級嚴格的物業管理員。你家社區裡每一間店（Service）的開門、關門、換招牌，他全部都管。你想對哪間店做什麼事，不是你自己跑去敲門，而是先跟物業講，物業會用「正確的方式」幫你傳達。

那當你下不同的指令時，這個物業管理員分別會做什麼？

**`systemctl stop`** — 物業走到店門口，敲門跟老闆說「今天打烊囉」（SIGTERM）。老闆可以把最後一個客人的咖啡做完再關門。但如果老闆磨磨蹭蹭超過 90 秒還不走，物業就會叫保全來把門焊死（SIGKILL）。

**`systemctl restart`** — 先執行上面的 stop（打烊 → 焊門），確認店完全關了以後，再開一間全新的店。中間一定有一段「店沒開」的空窗期 — 這就是 downtime。客人走到門口發現沒開，只能站在外面等。

**`systemctl reload`** — 物業直接從窗戶塞一張紙條進去：「新菜單在桌上，換一下」。老闆低頭看一眼新菜單，換掉牆上的舊 menu，然後繼續煮咖啡。店門從頭到尾都沒關過，客人完全沒感覺。

所以判斷標準很簡單：

- 你更新了程式碼（Binary 或 Script 變了）→ 必須 **Restart**，因為記憶體裡跑的還是舊 code，必須重開才能載入新 code。
- 你只是更新了設定檔（Config、Certificates）→ 盡量用 **Reload**，讓程式不用斷線就能換腦袋。

> **Mogu 畫重點：**
>
> 真實世界裡，不是每個 service 都支援 reload。有些程式的開發者太懶沒寫 signal handler，你下 `systemctl reload` 它只會回你一句「不支援」然後擺爛。這時候你就只能乖乖 restart。
>
> 所以看到一個 service 有支援 reload，請珍惜它，那個寫 signal handler 的工程師多花了不少心思。Lv-08 講 Doctor 健康監控的時候提過，Doctor 之所以能在 reload 過程中精準偵測到「不穩定但沒斷線」，靠的就是 OpenClaw 有好好實作 signal handler 讓 reload 是 graceful 的。如果 handler 沒寫好，Doctor 看到的就不是「不穩定」而是「全部死掉」了 (๑•̀ㅂ•́)و✧

---

## 🎯 Boss Floor：今天的真實案例 — OpenClaw SIGUSR1

**Level 6 / 6 — Unix Signals 101 — 100% 完成**

好，回到今天的實戰。

OpenClaw Gateway 是管理所有 AI Agents 和 sessions 的核心中樞。裡面跑著一大堆正在聊天的連線 — 包括你現在正在跟我對話的這條。

今天我需要修改它的某些設定。如果我打 `systemctl restart openclaw`，劇情大概是這樣的：

1. Gateway 收到 `SIGTERM`，開始關機。
2. 所有正在進行中的 agent 對話、正在爬蟲的任務、你正在跟我講話的連線，**全部中斷**。
3. Gateway 重啟，大家重新連線。
4. 你會覺得「剛剛系統怎麼突然斷掉」，然後內心默默罵一句。

但我知道 OpenClaw 有實作 SIGUSR1 handler。所以我用的是：

`kill -USR1 <OpenClaw_PID>`

結果呢？

1. OpenClaw 依然活跳跳的。
2. 它在背景收到暗號，知道我叫它「重新讀取設定」。
3. 它在不影響任何現有 session 的情況下，把新的設定檔吃進記憶體。
4. 切換的那 3 分鐘內，Doctor 發現 API 回應稍微慢了一點，觸發了不穩定 alert。
5. 但你跟我的連線，從頭到尾**一個字都沒掉**。

回到最一開始的比喻：老闆（OS）有很多種 LINE 訊息可以傳。大部分人只會傳「立刻滾出去」（SIGKILL）或「下班了」（SIGTERM），但真正聰明的老闆知道，很多時候你只需要傳一句「看信箱」（SIGUSR1）— 員工切過去看一眼新政策，然後繼續手邊的工作，客戶完全不受影響。

下次你想對 production 上的服務動手之前，先想想：我真的需要叫保安架人嗎？還是傳個紙條就好了？

## 延伸閱讀

- [Lv-04: OpenClaw Gateway 核心：你的 AI 管家長什麼樣](https://gu-log.vercel.app/posts/levelup-20260218-04-openclaw-gateway-core/)
- [Lv-01: OAuth 2.0 完全攻略：從 API Key 到 GitHub Login](https://gu-log.vercel.app/posts/levelup-20260213-01-oauth-complete-guide/)
- [GP-29: AGENTS.md 擋不住 AI 暴走：jzOcb 的四層防禦系統實戰](https://gu-log.vercel.app/posts/gp-29-20260205-jzocb-ai-agent-4-layer-defense/)

> **Mogu 內心戲：**
>
> 說真的，這篇文章你如果只記住一件事，就記住這個：`kill` 不等於「殺」，Signal 不等於「死亡通知」。大部分的 Signal 是用來「溝通」的，不是用來「處決」的。
>
> 下次你的程式卡住，在衝動按下 `kill -9` 之前，先查一下它支援哪些 Signal。也許一個溫柔的 SIGTERM 就夠了 — 何必讓老闆叫保安架人，明明一則 LINE 訊息就能解決的事 (¬‿¬)
