實體主機程式崩潰診斷:驗證核心傾印的擷取流程
業務程序突然終止時,重新啟動服務只能恢復運作,無法解釋崩潰原因。如果日誌只留下訊號編號,還需要崩潰當下的呼叫堆疊與對應建置,才能定位問題。本文在 Ubuntu Server 24.04 上建立受控的核心傾印擷取流程,先以隔離程序完成驗收,再決定是否為業務服務啟用。
AMDSSH 實體主機可選用 128GB 或 256GB ECC 記憶體,但不應依整機容量無條件保存程序傾印。core 檔案可能包含密碼、請求本文與業務資料;擷取範圍、容量限制及存取權限必須一併設定。
一、確認崩潰類型與現有收集器
付款後,在控制台查看連線憑證,並透過 SSH 登入。以下操作需要管理員權限;測試前先確認系統、磁碟空間與核心傾印的處理入口:
cat /etc/os-release
df -h /var/lib
sysctl kernel.core_pattern
sudo journalctl -b -p warning --no-pager
當 kernel.core_pattern 以豎線開頭時,核心會將傾印交給使用者空間的處理程式,而不是直接寫入目前目錄。系統可能已安裝其他崩潰收集器,必須記錄原始設定值與相關套件,不能疊加兩套處理入口後就假定兩者都能正常運作。
先區分主動結束、訊號終止與記憶體不足導致的程序終止。SIGSEGV、SIGABRT 通常會觸發核心傾印,SIGKILL 則不會。服務狀態顯示異常終止,不代表已產生可下載的檔案。
core dump 是程序記憶體的證據,不是一般執行日誌。不得放在公開目錄,也不應未經檢查就附加至支援案件或程式碼儲存庫。
安裝本文使用的工具;若套件管理員提示將取代現有收集器,應先確認影響,再明確選擇其中一種機制:
sudo apt-get update
sudo apt-get install systemd-coredump gdb gcc
sysctl kernel.core_pattern
systemctl status systemd-coredump.socket --no-pager
安裝後,處理入口應指向 systemd 的傾印處理程式。通訊端負責接收,處理服務則按需啟動;沒有常駐的擷取程序,不代表功能失效。
二、為擷取流程設定容量上限
建立 /etc/systemd/coredump.conf.d/60-controlled-capture.conf,寫入以下設定:
[Coredump]
Storage=external
Compress=yes
ProcessSizeMax=256M
ExternalSizeMax=256M
MaxUse=512M
KeepFree=2G
這是用於小型測試程序的驗收配額,不是高記憶體用量業務的建議值。ProcessSizeMax 限制可供處理的傾印大小,ExternalSizeMax 限制外部儲存的大小;超過限制時,可能只留下事件中繼資料,沒有完整的記憶體檔案。
MaxUse 與 KeepFree 用於控制外部傾印目錄的空間用量,不保證磁碟空間一定會被保留,也不控制日誌資料庫的容量。多個程序同時崩潰仍可能造成空間壓力,因此需要一併監控所在檔案系統的剩餘空間。
後續啟動的處理程序會讀取設定,可用以下命令核對合併後的結果:
systemd-analyze cat-config systemd/coredump.conf
為業務啟用前,應先量測具代表性的程序所用記憶體與實際傾印大小,再調整上限。不能因為測試程式通過驗收,就認為使用大量記憶體的資料庫也能完整保存傾印。
三、以隔離服務驗證實際崩潰
建置不含業務資料的測試程式
在 /tmp/crash-lab.c 寫入最小測試程式:
#include <stdlib.h>
int main(void)
{
abort();
}
建立專用的系統帳號並編譯程式。-g 保留除錯資訊,-O0 則方便觀察測試呼叫堆疊:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin crashlab
sudo install -d -m 0755 /usr/local/libexec
sudo gcc -g -O0 -o /usr/local/libexec/crash-lab /tmp/crash-lab.c
若帳號已存在,應先確認其用途,不要重複建立。接著建立 /etc/systemd/system/crash-lab.service:
[Unit]
Description=Isolated core dump capture test
[Service]
Type=oneshot
User=crashlab
ExecStart=/usr/local/libexec/crash-lab
LimitCORE=infinity
Restart=no
這裡的 infinity 只放寬測試服務的程序限制,收集器仍受上一節的配額約束。不要將它設成所有服務的全域預設值。
sudo systemctl daemon-reload
sudo systemctl start crash-lab.service
sudo systemctl status crash-lab.service --no-pager
sudo coredumpctl --no-pager list /usr/local/libexec/crash-lab
啟動命令回報失敗是預期結果。應能看到與測試執行檔相符的崩潰紀錄;若沒有,請依序檢查處理入口、服務限制、收集器日誌及磁碟空間,不要對正式環境中的程序傳送崩潰訊號。
四、驗收傾印檔案與除錯材料
從清單記下本次測試的 PID,在同一個管理員 shell 中輸入,將傾印匯出至專用的私人目錄:
sudo -i
read -r -p 'Crash PID: ' CRASH_PID
umask 077
install -d -m 0700 /root/crash-evidence
coredumpctl info "$CRASH_PID"
coredumpctl dump "$CRASH_PID" --output=/root/crash-evidence/test.core
gdb -q -batch \
-ex 'set pagination off' \
-ex 'thread apply all bt' \
/usr/local/libexec/crash-lab /root/crash-evidence/test.core
exit
先核對事件時間、執行檔、訊號與儲存狀態,避免因 PID 重複使用而取錯紀錄。驗收結果應顯示 SIGABRT,且呼叫堆疊能對應到測試程式的 main;系統函式庫的堆疊框架缺少原始碼,並不影響測試流程的驗證。
如果只有中繼資料、檔案無法匯出,或出現截斷提示,都不能視為完整擷取成功。coredumpctl 能列出事件,也不保證外部檔案仍然存在。
實際業務還需要封存崩潰當下的執行檔、共用函式庫與對應的除錯符號。更新後的同名二進位檔不等於原始建置,可使用 readelf -n 核對 Build ID,並記錄套件版本與建置識別碼。
不要直接使用 bt full 輸出區域變數。先查看基本呼叫堆疊;確實需要檢查變數時,應在受限環境中操作,並逐項審查匯出的文字。複製二進位檔時也應保存雜湊值,避免分析期間材料遭到替換。
五、業務導入與撤銷驗收
為業務服務啟用時,透過該服務自己的 drop-in 設定 LimitCORE,執行 daemon-reload 後,再於受控變更中重新啟動服務。既有程序不會因設定檔修改而自動套用新限制,應檢查實際程序的 /proc/PID/limits。
導入檢查至少應包含四項:
- 自動重新啟動是否造成連續傾印,目錄空間用量是否受控。
- 配額是否允許具代表性的業務程序保存完整檔案。
- 建置封存是否能提供對應的二進位檔、函式庫與除錯符號。
- 匯出目錄是否僅允許授權人員存取,材料是否包含敏感內容。
測試結束後,刪除測試服務檔案、程式及原始碼,並執行 daemon-reload 與 reset-failed。測試傾印應依 coredumpctl info 顯示的實際路徑個別處理,不要使用萬用字元清空整個傾印目錄。
若要撤銷擷取策略,請移除本文新增的設定檔;若需恢復原有收集器,應透過套件設定還原,並再次核對 kernel.core_pattern,不能憑記憶拼湊處理入口。最後記錄一次完整驗收:崩潰事件可查詢、檔案可匯出、呼叫堆疊對應正確建置、材料存取受到限制。如此留下的才是可複核的故障證據,而不是無人驗證的開關。
常見問題
啟用核心傾印後,每次異常結束都會留下檔案嗎?
不會。程式主動結束與 SIGKILL 通常不產生傾印;可產生傾印的訊號也會受到程序限制、擷取策略、檔案大小及磁碟空間影響,應以隔離測試驗證完整流程。
GDB 能開啟傾印檔案,就代表診斷資料完整嗎?
不是。還須取得崩潰當時的執行檔、共用函式庫與除錯符號,並確認傾印未被截斷。呼叫堆疊必須能對應正確的建置版本。