章節概覽#
本章深入探討 process、kernel 與系統資源之間的關係。系統有三種基本硬體資源:CPU、memory 與 I/O。Process 會競爭這些資源,而 kernel 的工作就是公平地分配它們。此外,kernel 本身也是一種軟體資源,process 透過它來建立新 process 和進行 inter-process communication。
本章介紹的許多工具屬於 performance-monitoring tools,在系統變慢時特別有用。但作者建議不要過度執著於效能調校,而是專注理解工具實際測量的內容,藉此深入了解 kernel 如何運作。
8.1 追蹤 Process(Tracking Processes)#
top 指令#
ps 指令只能列出某一時間點的 process 快照,無法顯示 process 隨時間的變化。top 提供互動式介面,每秒更新一次,預設將最活躍的 process(佔用最多 CPU 時間)排在最上方。
常用按鍵指令:
- Spacebar — 立即更新顯示
- M — 依 resident memory 使用量排序
- T — 依累計 CPU 使用時間排序
- P — 依目前 CPU 使用率排序(預設)
- u — 只顯示某個使用者的 process
- f — 選擇要顯示的統計欄位
- ? — 顯示所有
top指令的摘要
top的按鍵指令是區分大小寫的。類似工具atop和htop提供更多增強功能。
8.2 用 lsof 查找開啟的檔案(Finding Open Files with lsof)#
lsof 指令可列出系統中所有開啟的檔案及使用它們的 process。由於 Unix 非常重視檔案,lsof 是排查問題最實用的工具之一。它不僅能列出一般檔案,還能顯示 network resources、dynamic libraries、pipes 等。
8.2.1 讀取 lsof 輸出#
lsof 輸出的主要欄位:
- COMMAND — 持有檔案的 process 名稱
- PID — Process ID
- USER — 執行 process 的使用者
- FD — 檔案用途或 file descriptor 編號
- TYPE — 檔案類型(regular file、directory、socket 等)
- DEVICE — 裝置的 major 和 minor number
- SIZE/OFF — 檔案大小
- NODE — 檔案的 inode number
- NAME — 檔案名稱
以 root 身份執行
lsof可以看到更多資訊。
8.2.2 使用 lsof#
兩種基本使用方式:
- 列出所有並 pipe 到
less搜尋 - 使用 command-line options 縮小範圍
# 顯示 /usr 及其子目錄下開啟的檔案
$ lsof +D /usr
# 列出特定 PID 的開啟檔案
$ lsof -p pid
lsof高度依賴 kernel 資訊。如果進行了 distribution update(更新 kernel 與 lsof),更新後的 lsof 可能要到重啟後才能正常運作。
8.3 追蹤程式執行與 System Call#
當程式一啟動就立刻崩潰時,top 和 lsof 都無法幫上忙。這時需要使用 strace(system call trace)和 ltrace(library trace)。
8.3.1 strace#
System call 是 user-space process 要求 kernel 執行特權操作(如開啟、讀取檔案)的機制。strace 會印出 process 發出的所有 system call。
# 追蹤 cat 指令的 system call
$ strace cat /dev/null
# 追蹤 daemon 及其子 process
$ strace -o crummyd_strace -ff crummydstrace 輸出的關鍵部分:
- 開頭會看到
execve()和brk()(記憶體初始化) - 中間大量的 shared library 載入(通常可忽略)
- 尾端是實際的檔案操作,如
openat()、read()、close()
當指令遇到錯誤時,strace 能顯示精確的錯誤資訊。例如開啟不存在的檔案會回傳 -1 ENOENT (No such file or directory)。
找不到缺失檔案是 Unix 程式最常見的問題。當 system log 無法提供有用資訊時,
strace特別適合用來追蹤缺失的檔案。
8.3.2 ltrace#
ltrace 追蹤 shared library call,輸出格式類似 strace,但它不追蹤 kernel 層級的操作。由於 library call 數量遠多於 system call,務必過濾輸出。
ltrace無法用於 statically linked binaries。
8.4 Thread#
在 Linux 中,部分 process 會被分割為多個 thread。Thread 與 process 很相似,有自己的 thread ID (TID),kernel 也會排程和執行它們。但不同於各自獨立的 process,同一 process 內的所有 thread 會共享系統資源和部分記憶體。
8.4.1 Single-Threaded 與 Multithreaded Process#
- Single-threaded — 只有一個 thread 的 process
- Multithreaded — 有多個 thread 的 process
Multithreaded 的主要優勢:
- Thread 可同時在多個 processor 上執行,加速運算
- Thread 啟動比 process 更快
- Thread 間透過 shared memory 通訊比 process 間透過 pipe/socket 更有效率
8.4.2 檢視 Thread#
# 顯示 process 及其 thread
$ ps m
# 顯示 PID、TID 和指令
$ ps m -o pid,tid,command在 ps m 的輸出中,PID 欄有數字的行代表 process,PID 欄為 dash 的行代表該 process 的 thread。Single-threaded process 的 TID 與 PID 相同。
top預設不顯示 thread,需按 H 鍵開啟。大多數資源監控工具也需要額外設定才能顯示 thread。
8.5 資源監控入門(Introduction to Resource Monitoring)#
本節討論 processor (CPU) time、memory 和 disk I/O 的監控,涵蓋系統層級和 per-process 層級。
大多數 Linux 系統在預設設定下已有良好效能。與其追求效能調校,不如專注觀察 kernel 如何在 process 之間分配資源。
8.5.1 測量 CPU Time#
使用 top -p pid 可監控特定 process 的 CPU 使用情況。使用 time 指令可了解指令的 CPU 消耗:
$ time ls
real 0m0.442s
user 0m0.052s
sys 0m0.091s三種時間的意義:
- User time (user) — CPU 執行程式自身程式碼的時間
- System time (sys) — kernel 為 process 工作的時間(如讀寫檔案、目錄操作)
- Real time (real) — 又稱 elapsed time,程式從開始到結束的總時間
Real time 減去 user time 和 system time,可大致估算 process 等待系統和外部資源的時間。
8.5.2 調整 Process 優先權(Adjusting Process Priorities)#
Kernel 依據 scheduling priority(範圍 -20 到 20,-20 為最高優先權)來排程每個 process。
- PR 欄 — kernel 目前的排程優先權
- NI 欄 — nice value,使用者可控制的部分
Nice value 預設為 0。數值越高,process 對其他 process 越「友善」(讓出更多 CPU 時間)。
# 將 process 的 nice value 設為 20(最低優先權)
$ renice 20 pid在現代單一使用者的桌面系統上,通常不太需要調整 nice value。它在多使用者共用機器的年代更為重要。
8.5.3 用 Load Average 衡量 CPU 效能#
Load average 是目前準備執行的 process 平均數量,包含正在執行和等待 CPU 的 process。
$ uptime
... up 91 days, ... load average: 0.08, 0.03, 0.01三個數字分別代表過去 1 分鐘、5 分鐘、15 分鐘的 load average。
解讀方式:
- Load average 接近 0 — CPU 很閒
- Load average 接近 1 — 大約有一個 process 持續使用 CPU
- 對於多核心系統,load average 等於核心數代表所有核心都剛好滿載
處理高 load average:
- 高 load average 不一定代表系統有問題。若系統有足夠的 memory 和 I/O 資源,仍可能運作良好
- 若 load average 很高且系統明顯變慢,可能是遇到 memory performance 問題,kernel 開始 thrash(頻繁地在 disk 和 memory 之間交換資料)
8.5.4 監控記憶體狀態(Monitoring Memory Status)#
使用 free 指令或查看 /proc/meminfo 可檢查系統記憶體狀態。
記憶體運作原理:
- CPU 透過 MMU(Memory Management Unit)管理記憶體存取
- Kernel 將 process 使用的記憶體切割成 page(通常為 4KB)
- Kernel 維護 page table,將虛擬位址對應到實體位址
- 採用 on-demand paging(demand paging):不需一次載入所有 page,而是在需要時才載入
# 查看系統的 page size
$ getconf PAGE_SIZE
4096Page Fault 的兩種類型:
- Minor page fault — 所需 page 在記憶體中但 MMU 不知道位置,kernel 告知 MMU 即可,影響很小
- Major page fault — 所需 page 不在記憶體中,kernel 必須從 disk 載入,大量 major page fault 會嚴重拖慢系統
# 查看指令的 page fault 數量
$ /usr/bin/time cal > /dev/null
# 查看特定 PID 的 page fault
$ ps -o pid,min_flt,maj_flt 20365當記憶體不足時,kernel 會開始將 working memory 的 page swap 到 disk,導致 thrashing,系統效能會急遽下降。
8.5.5 用 vmstat 監控 CPU 與記憶體效能#
vmstat 是最古老的系統監控工具之一,開銷極小,適合快速了解系統的 swap、I/O 和 CPU 使用情況。
# 每 2 秒報告一次統計
$ vmstat 2輸出分為幾個類別:
- procs — process 資訊
- memory — 記憶體使用(swpd、free、buff、cache)
- swap — page swap in/out
- io — disk I/O(blocks in/out)
- system — kernel context switch 次數
- cpu — CPU 時間分佈(us=user、sy=system、id=idle、wa=waiting for I/O)
當記憶體耗盡時,so(swap out)欄會出現活動,buffer 和 cache 大小也會下降。
8.5.6 I/O 監控#
iostat#
iostat 專門用於 per-device I/O 監控,比 vmstat -d 更易使用。
$ iostat
$ iostat -p ALL # 顯示所有分割區
$ iostat -d 2 # 每 2 秒更新 device 報告每個 device 的輸出欄位:
- tps — 每秒平均資料傳輸次數
- kB_read/s — 每秒平均讀取 KB
- kB_wrtn/s — 每秒平均寫入 KB
- kB_read — 總讀取 KB
- kB_wrtn — 總寫入 KB
iotop#
iotop 可查看個別 process 的 I/O 使用情況,類似 top 的介面。它顯示的是 TID(而非 PID),是少數以 thread 為單位顯示的工具之一。
I/O scheduling class(排程類別):
- be (Best effort) — 大多數 process 使用此類別
- rt (Real time) — 優先於其他所有 I/O
- idle — 只在沒有其他 I/O 時才執行
8.5.7 用 pidstat 做 Per-Process 監控#
pidstat 以 vmstat 風格顯示單一 process 的資源消耗,不會像 top 那樣每次更新就覆蓋前一次的輸出。
# 監控 PID 1329,每秒更新
$ pidstat -p 1329 1pidstat 還支援 -r(memory)和 -d(disk)選項進行更多監控。
8.6 Control Groups (cgroups)#
cgroup(control group)是 Linux kernel 的功能,可將多個 process 放入一個群組,以群組為單位管理和限制資源消耗。例如限制一組 process 可累計使用的記憶體上限。
建立 cgroup 後,可加入 process 並使用 controller 控制行為:
- cpu controller — 限制 processor time
- memory controller — 限制記憶體使用
- pids controller — 限制 process 數量
雖然 systemd 大量使用 cgroup 功能,但 cgroup 是 kernel space 的功能,不依賴 systemd。
8.6.1 區分 cgroup 版本#
目前有 cgroups v1 和 cgroups v2 兩個版本,可能同時存在於系統中:
- v1 — 每種 controller(cpu、memory 等)有自己獨立的 cgroup 集合。一個 process 可同時屬於多個 cgroup(每個 controller 一個)

Figure 8-1: cgroups v1
- v2 — 一個 process 只能屬於一個 cgroup,但一個 cgroup 可以有多個 controller

Figure 8-2: cgroups v2
由於 v1 正在被淘汰,本書主要討論 cgroups v2。
# 查看目前 shell 的 cgroup
$ cat /proc/self/cgroup8.6.2 檢視 cgroups#
cgroup 完全透過 filesystem 存取,通常掛載在 /sys/fs/cgroup(v2)或 /sys/fs/cgroup/unified。
# 查看 cgroup 目錄
$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-2.scope
$ cd /sys/fs/cgroup/user.slice/user-1000.slice/session-2.scope/
# 查看 cgroup 內的 controller
$ cat cgroup.controllers
memory pids
# 查看 thread 數量
$ cat pids.current
# 查看記憶體上限
$ cat memory.max8.6.3 操作與建立 cgroups#
# 將 process 加入 cgroup
# echo pid > cgroup.procs
# 限制 cgroup 的最大 PID 數
# echo 3000 > pids.max建立 cgroup 的規則:
- Process 只能放在 leaf(最外層)cgroup 中
- 子 cgroup 的 controller 必須存在於父 cgroup 中
- 需透過
cgroup.subtree_control檔案明確指定子 cgroup 可用的 controller
8.6.4 查看資源使用#
即使沒有啟用 controller,也可以透過 cgroup 查看資源使用:
# 查看 CPU 使用統計
$ cat cpu.stat
usage_usec 4617481
user_usec 2170266
system_usec 2447215啟用 controller 後,memory.current 和 memory.stat 等檔案可提供更詳細的資訊。
8.7 延伸主題(Further Topics)#
資源監控工具種類繁多,因為不同類型的資源以不同方式被消耗。本章涵蓋了 CPU、memory 和 I/O 作為系統資源,被 process、thread 和 kernel 所使用。
這些工具存在的原因是資源是有限的,系統的各元件必須努力減少資源消耗才能維持良好效能。
其他值得探索的主題:
- sar (System Activity Reporter) — 類似
vmstat但可記錄歷史資料,適合回溯分析過去的系統事件 - acct (process accounting) — 可記錄 process 及其資源使用
- Quotas — 可限制使用者的 disk space 使用量
如果對系統調校和效能分析有更深入的興趣,推薦 Brendan Gregg 的 Systems Performance: Enterprise and the Cloud(第二版,Addison-Wesley, 2020)。