診斷心法:讀權威狀態,不靠肉眼猜表象
診斷一個 Linux 問題時,第一個動作不是猜「這看起來像什麼」,而是問「這件事的權威狀態在哪裡、我怎麼去讀它」。畫面上的現象、終端機捲過的輸出、一個視窗長什麼樣,都是表象;表象會騙人。真正能定案的是系統裡記錄這件事的那個權威來源——程式自己的 log、服務註冊表、核心與 systemd 的狀態、資源用量。把判斷建立在權威狀態上,而不是肉眼看到的樣子,是快速且不猜錯的除錯的核心。
這篇講的是一套判讀紀律,不是某個特定工具。後面幾篇(遠端連線與終端機問題、機器連不到或起不來、程序、服務與狀態怎麼判)是這套紀律在各種具體情境的應用。
表象會騙人:一個判斷被畫面帶偏兩次的實例
一個具體案例最能說明為什麼不能靠肉眼。在一次桌面 shell(畫桌面 UI 的圖形程式,不是 bash/zsh 那種命令列 shell)的除錯裡,畫面中央出現一個「輸入密碼」的覆蓋層,配著時鐘、天氣、通知的整片儀表板。第一眼的判斷很自然:螢幕被鎖住了。
接著幾個看似合理的檢查反而把判斷帶得更偏:loginctl 查不到這個 session 的 LockedHint、pgrep 找不到任何獨立的鎖屏程式、那個 shell 的 CLI 也沒有 lock 指令。三個訊號湊起來,得出一個「更正」的結論:這不是真的鎖,只是一個長得像鎖屏的儀表板面板。
這個「更正」是錯的。真正定案是靠讀那個 shell 自己寫的 log:log 裡明明白白有鎖屏模組被載入、有 idle 計時器在數秒數、時間到就觸發鎖定。它是一個真的螢幕鎖,走的是 Wayland 的 session-lock 協議。
為什麼前面三個檢查會誤導?因為它們讀的是錯的權威來源。loginctl 的 LockedHint 是 logind(systemd 的登入管理)那一層的鎖定狀態,而這個鎖走的是 Wayland 合成器(compositor,負責把視窗合成到螢幕、管輸入輸出的核心程式)那一層的協議,兩者是獨立機制——查 logind 對合成器層的鎖天生查不到,不是「沒鎖」,是查錯地方。pgrep 找不到獨立程式,是因為鎖屏畫面由 shell 主程式在自己的行程內畫,本來就沒有另一個可執行檔可抓。真正記錄「有沒有鎖、為什麼鎖」的權威來源,是那個 shell 的 log;讀到它,一次就定案。
肉眼加上讀錯層的檢查,猜錯了兩次;讀對權威來源,一次就對。教訓不是「那些工具沒用」,是要先確認你讀的是不是這件事的權威狀態。
每種問題都有它的權威狀態來源
除錯的第一步,是為眼前的現象找到記錄它的權威來源。不同類別的問題,權威來源不同:
| 問題類別 | 權威狀態來源 | 讀它的工具 |
|---|---|---|
| 某程式的行為 | 那個程式自己的 log 檔 | 程式的 log 路徑、journalctl -u <服務> |
| 服務由誰提供 | D-Bus / socket 的服務註冊 | busctl、ss、lsof |
| 登入 / 鎖定狀態 | logind | loginctl show-session |
| 服務有沒有在跑 | systemd unit 狀態 | systemctl status、systemctl is-active |
| 程式有沒有活著 | 行程表(比對正確的 comm 名) | pgrep -x、ps |
| 網路通不通 | 介面 / 路由 / 鄰居表 | ip -brief a、ip neigh、ss |
| 磁碟 / 記憶體 | 檔案系統與記憶體用量 | df -h、du -sh、free |
| 核心 / 硬體 / 被殺行程 | kernel ring buffer | dmesg、journalctl -k |
| 程式 log 沉默時的 syscall | 系統呼叫層 | strace -f -e trace=file |
這張表的用法不是背它,是養成一個反射:看到現象先問「這件事的權威狀態記在哪張表裡」,再去讀那張表,而不是從畫面推測。下面幾個常見的判錯,都是讀了表象而不是權威來源。
讀對權威來源、但查詢條件要對
有時權威來源對了,還是會被誤導——因為查詢的條件寫錯。判程式活著沒,行程表是對的權威、pgrep 是對的工具,但你得比對它實際的行程名:一個程式可能以 symlink 的短名在跑,用你以為的名字 pgrep 就掃不到、誤判成掛了。判服務由誰提供,權威是服務註冊表而非畫面(送一則通知看畫面有沒有跳不可靠——沒跳可能是勿擾吃掉或根本沒送出)。這兩類的具體查法(pgrep -x、busctl 查 D-Bus 擁有者)見 程序、服務與狀態怎麼判。重點是:權威來源對,還要問對地方、用對條件。
卡住是資源問題還是相容問題:先看資源,別先怪相容性
一個耗時的操作中途停住時,很容易直接跳到「是不是這個平台不相容 / 這個東西在這台機器上跑不起來」。但這個結論成本很高(可能讓你放棄一條其實可行的路),而它的權威狀態很好查。一次原始碼編譯跑到一半停住,第一個該看的是資源:df -h 看磁碟是不是滿了、記憶體是不是被吃光——一次實際的案例就是主機磁碟寫滿把編譯中途打斷,清出空間後同一份原始碼接著編就過,跟平台相容性完全無關。先讀資源狀態排除掉最廉價的解釋,再去懷疑相容性這種昂貴的結論。
讀程式自己的 log:從症狀往上游找
當現象是「某個程式行為不對」,它自己的 log 幾乎總是比終端機捲過的畫面更接近真相。很多程式在終端機只印一段摘要,卻同時把詳細執行紀錄寫進一個 log 檔或系統日誌;當畫面上的訊息不足以定位時,那份 log 裡往往就有明確答案。
找 log 的常見去處:程式自己的 log 檔(常在 ~/.local/state/<程式>/ 或 ~/.cache/<程式>/ 底下)、systemd 服務的 journalctl -u <服務名>、或程式啟動時印出的 log 路徑。找到之後,關鍵是用症狀當關鍵字往上游搜——grep -iE 'error|fail|not found|does not exist' <log> 挑出異常行,或在 less 裡用 ?pattern 往回找「第一個」異常(不是停在最後一個下游錯)。一個指令因為前面某個檔案不存在而失敗,終端機可能只報一個看似無關的下游錯誤,但 log 裡會有那句 File does not exist 直指源頭。一個實際案例:某 shell 換了配色卻沒生效,畫面上什麼錯都沒有,是它的 log 裡一句「讀取 scheme 檔失敗:檔案不存在」點出根因——原來那個檔在 shell 啟動當下還沒被建出來。畫面沉默,log 說話。
這一層跟 可除錯的 bootstrap 是一體兩面:那篇談怎麼讓你自己寫的腳本產生一份可診斷的 log,這裡談除錯時怎麼去讀程式自己的 log。兩邊的共同紀律是:不要只盯著終端機捲動,去找那份持久的、詳細的權威紀錄。
遠端除錯反而逼出好紀律
透過 SSH 遠端除錯時,你看不到那台機器的畫面——這個限制反而是好事。看不到畫面,你就沒得靠肉眼猜,只能去讀權威狀態:查 log、查服務註冊、查行程表、查資源。很多在本地會犯的「看畫面就下結論」的錯,在遠端因為根本沒畫面可看而自動被避開。
反過來說,在本地(或看得到畫面的 VM)除錯時,畫面的存在是個誘惑:它讓你以為看到了就懂了。前面那個鎖屏誤判,正是發生在「看得到畫面」的情境——畫面上的密碼框太有說服力,反而蓋過了去讀 log 的動作。把遠端那套「沒有畫面、只信權威狀態」的紀律,也用在本地,就不會被畫面帶偏。
判讀紀律:四步
把上面的東西收成一套每次都能跑的流程:
- 描述症狀:現象是什麼,先講清楚,不要在這步就急著下結論(「畫面出現密碼框」,不是「螢幕鎖了」)。
- 定位權威來源:這件事的權威狀態記在哪——log、服務註冊、logind / systemd、行程表、資源用量(用上面那張表對照)。
- 用對的工具讀它:讀那個權威來源,不是讀畫面、不是讀終端機捲過的殘影。
- 權威跟表象矛盾時,信權威:如果讀到的權威狀態跟你肉眼的第一印象打架,信權威狀態、回頭修正第一印象——那個矛盾點通常就是你原本會猜錯的地方。
這套流程的價值不在任何單一工具,在於它讓你的判斷有一個可回溯的依據,而不是一串越猜越偏的直覺。
下一步
- 這套心法在遠端連線與終端機情境的應用,見 遠端連線與終端機問題。
- 機器連不到、或根本起不來時怎麼從權威狀態往下查,見 機器連不到或起不來。
- 程序在不在、服務歸誰、狀態怎麼判的具體招式,見 程序、服務與狀態怎麼判。
- 怎麼讓你自己的 bootstrap 腳本產生可讀的 log,見 可除錯的 bootstrap。