跳轉至

Linux 基礎 2:行程 (Process) 管理

目標:徹底理解「行程」。這是理解容器最關鍵的一塊 —— 因為容器本質上就是「一個或一群被特殊隔離的行程」。


1. 什麼是行程 (Process)?

程式 (Program) 是躺在磁碟上的一個檔案(例如 /usr/bin/nginx)。 行程 (Process) 是這個程式被載入記憶體、正在執行的一個實例。

比喻:食譜是「程式」,你照著食譜實際在廚房做菜的這次行動是「行程」。同一份食譜可以同時有很多人各自在做(同一個程式可以有很多行程)。

每個行程都有:

屬性 說明
PID (Process ID) 行程的唯一編號
PPID (Parent PID) 父行程的 PID(行程是由其他行程「生」出來的)
UID/GID 以哪個使用者/群組身分執行(決定權限)
狀態 執行中 (R)、睡眠 (S)、停止 (T)、殭屍 (Z) 等
環境變數、開啟的檔案、記憶體空間… 各自獨立

2. 行程樹:一切始於 PID 1

Linux 開機後,核心啟動的第一個行程 PID 1(現代系統通常是 systemd)。之後所有行程都是它的子孫,形成一棵樹。

graph TD
    init["PID 1<br/>systemd (init)"] --> sshd[sshd]
    init --> cron[cron]
    sshd --> bash["bash (你的 shell)"]
    bash --> vim[vim]
    bash --> ps[ps aux]
pstree           # 用樹狀圖看所有行程
pstree -p        # 連 PID 一起顯示

🔑 這個 PID 1 觀念對容器超級重要! 在一個容器裡,你的主程式(例如 nginx)會變成那個容器內的 PID 1。容器內看到的行程,跟主機看到的是「不同的視角」—— 這正是命名空間 (Namespace) 的魔法(第 4 章詳述)。


3. 查看行程:ps、top、htop

ps:某一瞬間的快照

ps aux            # 最常用:列出所有使用者的所有行程
ps aux | grep nginx   # 找特定行程
ps -ef            # 另一種常見格式(顯示 PPID)
ps -ef --forest   # 用樹狀顯示父子關係

ps aux 的欄位解讀:

欄位 意義
USER 哪個使用者跑的
PID 行程編號
%CPU / %MEM CPU 與記憶體使用率
VSZ / RSS 虛擬 / 實際記憶體用量
STAT 狀態(R 執行、S 睡眠、Z 殭屍…)
START 啟動時間
COMMAND 執行的指令

top / htop:即時動態監控

ps 是「某一瞬間的快照」,top / htop 則是每隔幾秒自動刷新的即時監控,用來看「這台機器現在忙不忙、誰在吃資源」。

top              # 即時刷新的行程監控(q 離開)
htop             # 更友善的彩色版(需安裝,強烈推薦)

看懂 htop 上半部的「儀表板」

htop 最上面那塊不是行程列表,而是整台機器的健康概況。最該先看懂的有三個:

① Load average:1.20 0.95 0.80(三個數字)

系統在 過去 1 / 5 / 15 分鐘 的平均「負載」——也就是同一時間有多少行程「正在用 CPU,或正在排隊等 CPU / 等磁碟 I/O」(Linux 把 R 執行中與 D 不可中斷的行程都算進去)。

  • 三個數字由近到遠,用來看趨勢:5.0 2.0 1.0(遞減)是剛爆量、正在回落;1.0 2.0 5.0(遞增)是負載持續上升、要注意。
  • 關鍵:要對照 CPU 核心數判讀(用 nproc 查核心數),而不是看絕對值:
機器 load average 解讀
4 核 4.0 剛好滿載(每核都有一個在跑)
4 核 8.0 過載,約一半行程在排隊等 CPU
4 核 1.0 很輕鬆(約用 25%)

口訣:load average ÷ 核心數 ≈ 1 就是滿載分界線。

② Tasks:32, 32 thr; 1 running

  • 32 = 目前系統的行程 (process) 數量。
  • 32 thr = 總執行緒 (thread) 數——一個行程可開多條執行緒,所以這個數字通常 ≥ 行程數。
  • 1 running = 此刻真正處於 R(執行中 / 可執行) 狀態的有幾個。其餘幾十個大多在 S(睡眠),在等事件(等網路、等鍵盤、等計時器),並不佔 CPU

所以「有幾十個 task」不代表機器很忙——重點是 running 有幾個,以及 load average。

③ Uptime

機器從開機到現在已連續運行多久(例如 up 5 days, 3:42)。排查問題時,若 uptime 很短,代表這台機器最近可能當機重開過。

此外上半部還有 每核一條的 CPU 使用率條(綠=使用者程式、紅=核心)、以及 Mem / Swp 記憶體條(Swap 一直在漲通常是壞訊號,代表實體記憶體不夠了)。

💡 看一台機器「忙不忙」,先看 load average 與 CPU/記憶體使用率。之後排查 K8s 節點問題(節點 NotReady、Pod 被驅逐)時,SSH 進去先看這幾個數字,就是同一套思路。


4. 前景、背景與作業控制

sleep 100              # 前景執行,佔住終端機
sleep 100 &            # 加 & 丟到背景執行
jobs                   # 看目前 shell 的背景作業
fg %1                  # 把背景作業 1 拉回前景
bg %1                  # 讓停住的作業在背景繼續
# Ctrl + Z             # 把前景行程「暫停」並丟到背景
# Ctrl + C             # 送出中斷訊號,通常會終止前景行程

nohup& 讓行程在你登出後繼續跑:

nohup ./long-task.sh > out.log 2>&1 &   # 登出也不會被殺掉

5. 訊號 (Signal):與行程溝通的方式

訊號是核心或其他行程「通知」某個行程的機制。你按 Ctrl+C 其實就是送了一個訊號。

訊號 編號 意義 能否被忽略/攔截
SIGTERM 15 禮貌地請你結束(預設) 可以,程式可做收尾(優雅關閉)
SIGKILL 9 強制立刻砍掉 ❌ 不能被攔截,核心直接終結
SIGINT 2 中斷(你按 Ctrl+C) 可以
SIGHUP 1 終端機關閉 / 請重載設定 可以
SIGSTOP 19 強制暫停,只能用 kill -STOP ❌ 不能被攔截
SIGTSTP 20 暫停(你按 Ctrl+Z 送出的其實是這個,不是 SIGSTOP) 可以,程式能攔截/忽略(例如某些程式讓 Ctrl+Z 失效)
kill 1234            # 預設送 SIGTERM(15),請 PID 1234 優雅結束
kill -9 1234         # 送 SIGKILL,強制砍掉(最後手段)
kill -SIGTERM 1234   # 明確指定訊號
killall nginx        # 依名稱砍掉所有 nginx 行程
pkill -f "my-script" # 依指令內容比對來砍

🔑 這跟 K8s 關係很大! 當 K8s 要關閉一個 Pod 時,它會先送 SIGTERM 給容器(給它時間優雅關閉,例如把連線處理完、存檔),等待一段寬限期 (terminationGracePeriodSeconds,預設 30 秒)後,若還沒結束才送 SIGKILL 強制終止。理解 SIGTERM/SIGKILL,你就懂了 Pod 的「優雅關閉 (Graceful Shutdown)」。


6. /proc:行程的真實面貌

每個行程在 /proc/<PID>/ 下都有一個「資料夾」,裡面是核心即時生成的行程資訊。

ls /proc/1/             # PID 1 的所有資訊
cat /proc/1/status      # 狀態、記憶體、UID...
cat /proc/1/cmdline     # 啟動指令
ls -l /proc/1/fd/       # 它開啟的所有檔案描述符
ls -l /proc/1/ns/       # ⭐ 它所屬的命名空間!(第 4 章關鍵)
cat /proc/1/cgroup      # ⭐ 它所屬的 cgroup!(資源限制)

🔑 先記住這兩行:/proc/<PID>/ns//proc/<PID>/cgroup。 容器的隔離 (namespace) 與限制 (cgroup),全都能在這裡「看到證據」。第 4 章我們會用它親手驗證「容器就是行程」。


7. 資源觀察

free -h          # 記憶體使用情況(人類易讀)
df -h            # 磁碟空間使用情況
du -sh ./*       # 目前目錄下各項目佔多少空間
uptime           # 系統運行多久 + 負載平均
vmstat 1         # 每秒刷新的系統資源統計

動手練習

  1. 看行程樹:執行 pstree -p,找出 PID 1 是誰。再開一個新的 shell,用 ps -ef --forest 觀察你的 bash 與其子行程的父子關係。
  2. 背景作業:執行 sleep 300 &,用 jobsps aux | grep sleep 觀察它。再用 kill 把它結束掉。
  3. 體驗訊號差異:
    sleep 1000 &           # 背景執行,記下它的 PID
    kill <PID>             # 送 SIGTERM,觀察它消失
    sleep 1000 &
    kill -9 <PID>          # 送 SIGKILL,比較差別
    
  4. 探索 /proc:挑一個正在跑的行程(例如你的 shell,echo $$ 可看自己的 PID),cat /proc/$$/statusls -l /proc/$$/ns/,看看它屬於哪些命名空間(先有印象,第 4 章會深入)。
  5. 監控:開 htop(或 top),找出最吃 CPU 和記憶體的行程。

本節檢核點

  • [ ] 能用自己的話分辨「程式」與「行程」。
  • [ ] 理解 PID / PPID 與行程樹,知道 PID 1 是 init(systemd)。
  • [ ] 理解「容器內的主程式是該容器的 PID 1」這個伏筆。
  • [ ] 會用 ps auxtop/htop 查看與監控行程。
  • [ ] 理解前景/背景、&Ctrl+CCtrl+Zjobsfg/bg
  • [ ] 能清楚說明 SIGTERM vs SIGKILL 的差別,並連結到 K8s 的優雅關閉。
  • [ ] 知道 /proc/<PID>/ns//proc/<PID>/cgroup 的存在與意義。

➡️ 下一節:使用者與權限