化)
電腦重啟不了排查實錄:3步搞定死機,兼顧性能優(yōu)化
剛升級完驅(qū)動,電腦重啟不了?別急著砸鍵盤。
版本升級后 API 全變了,內(nèi)核加載邏輯變了,舊配置直接沖突。
這時候硬重啟只是治標(biāo),我們要做的是定位瓶頸,順手做個性能優(yōu)化。
項目目標(biāo):構(gòu)建自動化故障診斷工具
很多兄弟一遇到“電腦重啟不了”,第一反應(yīng)就是拔電源。這沒錯,但下次呢?還是卡住。
我們需要一個輕量級的腳本,能在系統(tǒng)半死機狀態(tài)(比如還有網(wǎng)絡(luò),或者能進安全模式)下,快速收集關(guān)鍵日志,并給出重啟建議。
這個項目目標(biāo)很明確:快速定位:是硬盤壞了?內(nèi)存爆了?還是驅(qū)動沖突?
數(shù)據(jù)留存:把崩潰前的最后幾條日志抓下來,別丟。
溫和重啟:通過系統(tǒng)指令強制重啟,避免硬件損傷。這不是為了炫技,而是為了在下次故障發(fā)生時,你能像醫(yī)生看X光片一樣,一眼看出病灶。對于經(jīng)常跑大型編譯、虛擬機集群的開發(fā)者來說,這種“重啟不了”的代價是巨大的——丟掉的上下文、中斷的構(gòu)建,都是時間成本。
目錄結(jié)構(gòu):極簡主義,拒絕過度設(shè)計
別搞那種幾百個文件的框架,排查工具講究一個“快”。
我們在項目根目錄下只放三個核心文件:
project-root/
├── diag.py # 主診斷腳本
├── config.yaml # 閾值配置(內(nèi)存、CPU、磁盤IO)
└── logs/ # 自動生成的日志目錄└── crash_YYYYMMDD_HHMMSS.logdiag.py 是核心,config.yaml 讓你可以根據(jù)不同機器調(diào)整靈敏度。比如你跑的是高負載服務(wù)器,內(nèi)存閾值設(shè)高一點;如果是辦公本,設(shè)低一點。
logs 目錄必須存在,腳本會在啟動時自動創(chuàng)建,防止寫入失敗。
核心代碼實現(xiàn):Python 驅(qū)動的診斷邏輯
這里我們用 Python 3.9+ 實現(xiàn)。為什么選 Python?因為跨平臺,Windows、Linux、Mac 都能跑,而且?guī)熵S富。
核心依賴只有 psutil(監(jiān)控資源)和 pyyaml(讀配置)。
import psutil
import yaml
import os
import time
import subprocess
import logging
from datetime import datetime# 配置日志,輸出到控制臺和文件
log_file = flogs/crash_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log
os.makedirs(logs, exist_ok=True)
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler(log_file),logging.StreamHandler()]
)def load_config(path='config.yaml'):加載閾值配置try:with open(path, 'r') as f:return yaml.safe_load(f)except Exception as e:logging.error(f配置加載失敗: {e}, 使用默認值)return {'cpu_threshold': 95,'mem_threshold': 95,'disk_io_threshold': 90,'restart_timeout': 30}def check_system_health(cfg):檢查系統(tǒng)健康度返回: (is_healthy: bool, reason: str)cpu_percent = psutil.cpu_percent(interval=1)mem_percent = psutil.virtual_memory().percentdisk_io = psutil.disk_io_counters()# 計算磁盤IO使用率 (簡化處理,實際需結(jié)合讀寫速度)# 這里僅作為演示,真實場景需監(jiān)控特定分區(qū)io_percent = (disk_io.read_count + disk_io.write_count) % 100 # 偽代碼,實際需更復(fù)雜邏輯logging.info(f當(dāng)前狀態(tài) - CPU: {cpu_percent}%, MEM: {mem_percent}%, IO: {io_percent}%)if cpu_percent cfg['cpu_threshold']:return False, fCPU占用過高: {cpu_percent}%if mem_percent cfg['mem_threshold']:return False, f內(nèi)存占用過高: {mem_percent}%if io_percent cfg['disk_io_threshold']:return False, f磁盤IO瓶頸: {io_percent}%return True, System OKdef force_restart(reason):執(zhí)行強制重啟在Windows下使用 shutdown /r /t 0在Linux下使用 rebootlogging.warning(f觸發(fā)強制重啟: {reason})# 等待日志刷新time.sleep(2)if os.name == 'nt':# Windows: /r 重啟, /t 0 立即, /f 強制關(guān)閉應(yīng)用程序subprocess.call(['shutdown', '/r', '/t', '0', '/f'])else:# Linux/Macsubprocess.call(['reboot'])def main():logging.info(=== 電腦重啟不了診斷工具啟動 ===)cfg = load_config()# 循環(huán)監(jiān)控,直到系統(tǒng)健康或超時start_time = time.time()timeout = cfg.get('restart_timeout', 30)while time.time() - start_time timeout:is_healthy, reason = check_system_health(cfg)if not is_healthy:# 記錄詳細進程列表,方便事后分析top_procs = psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent'])with open(log_file, 'a') as f:f.write(f\n--- 崩潰前Top進程 ---\n)for p in sorted(top_procs, key=lambda x: x.info['cpu_percent'], reverse=True)[:10]:f.write(fPID:{p.pid} Name:{p.name()} CPU:{p.info['cpu_percent']}% MEM:{p.info['memory_percent']}%\n)force_restart(reason)breakelse:time.sleep(5)else:logging.info(監(jiān)控超時,未觸發(fā)重啟,系統(tǒng)可能已恢復(fù)或需人工干預(yù))if __name__ == '__main__':main()逐行解析關(guān)鍵點psutil.cpu_percent(interval=1):這個 interval=1 很關(guān)鍵。默認是0,返回的是上次調(diào)用的差值,第一次調(diào)用永遠是0。設(shè)為1秒,能拿到真實的瞬時負載。
日志雙寫:handlers 里同時加了 FileHandler 和 StreamHandler。文件用來存檔,屏幕用來實時看。如果系統(tǒng)卡死,屏幕可能沒反應(yīng),但文件通常還在寫(除非磁盤也掛了)。
進程快照:在觸發(fā)重啟前,top_procs 那一段是靈魂。它把當(dāng)時占用最高的10個進程PID和名字記下來。很多“重啟不了”是因為某個后臺服務(wù)(比如殺毒軟件、更新程序)死鎖了。有了這個日志,重啟后一查PID,立馬知道是誰在搞鬼。
跨平臺重啟:os.name 判斷系統(tǒng)。Windows 用 shutdown 命令是最穩(wěn)定的,比 PowerShell 腳本更底層,不容易被攔截。運行與測試:模擬“假死”場景
代碼寫完了,怎么測?總不能真等電腦死機吧?
我們用一個簡單的技巧:人為制造資源瓶頸。準(zhǔn)備環(huán)境:
安裝依賴:pip install psutil pyyaml編寫 config.yaml:
cpu_threshold: 50 # 故意設(shè)低,方便測試
mem_threshold: 50
disk_io_threshold: 50
restart_timeout: 10制造壓力:
打開另一個終端,運行一個簡單的死循環(huán)腳本,把CPU吃滿:
# stress_test.py
import time
while True:pass或者在 Windows 上打開任務(wù)管理器,啟動幾個高CPU占用的程序。運行診斷:
python diag.py預(yù)期現(xiàn)象:日志開始滾動,顯示 CPU 占用超過 50%。
5秒后(因為 sleep(5)),再次檢測。
如果持續(xù)超標(biāo),打印 Top 進程列表。
執(zhí)行 shutdown /r /t 0,電腦黑屏,重啟。避坑指南:殺毒軟件干擾:有些殺毒軟件會攔截 shutdown 命令,或者把 diag.py 標(biāo)記為可疑。測試前,把腳本目錄加入白名單。
日志寫入失?。喝绻疟PIO極高,logging.FileHandler 可能會阻塞。進階版可以用異步日志,但對于排查工具來說,同步寫入更安全,確保數(shù)據(jù)不丟。優(yōu)化擴展:從“重啟”到“性能優(yōu)化”
重啟只是止血,我們要的是性能優(yōu)化。
這個腳本目前只能做“重啟”,怎么讓它更智能?增加“優(yōu)雅退出”邏輯:
在強制重啟前,先嘗試發(fā)送 SIGTERM 給高占用進程。如果10秒內(nèi)沒退出,再 SIGKILL。
# 偽代碼
for proc in top_procs:try:proc.terminate()except:pass
time.sleep(10)
# 檢查是否退出,沒退出再 kill這樣能減少數(shù)據(jù)丟失風(fēng)險。歷史趨勢分析:
每次運行都把 CPU/MEM 數(shù)據(jù)追加到一個 CSV 文件。
重啟后,用 Python 讀這個 CSV,畫個折線圖。
你會發(fā)現(xiàn):是不是每次在編譯到 80% 的時候內(nèi)存就爆?如果是,那就是代碼內(nèi)存泄漏,或者編譯器配置問題。
這才是性能優(yōu)化的核心:數(shù)據(jù)驅(qū)動,而不是玄學(xué)調(diào)參。集成到 CI/CD:
在自動化測試服務(wù)器上,把這個腳本跑起來。
如果服務(wù)器重啟了,自動把 logs/crash_*.log 打包,通過郵件或企業(yè)微信發(fā)給開發(fā)者。
我見過一個團隊,靠這個功能,在一個周內(nèi)定位了三個導(dǎo)致服務(wù)器死機的 Java 線程死鎖問題。Stack Overflow 上有很多關(guān)于 OutOfMemoryError 的討論,但光看堆棧不夠,要看系統(tǒng)級的資源曲線。Windows 事件日志集成:
在 Windows 上,可以用 win32eventlog 庫讀取系統(tǒng)事件日志。
有些“重啟不了”是因為內(nèi)核藍屏(BSOD),但重啟后藍屏信息丟了。
腳本可以在啟動時,讀取最近一次的 BSOD dump 文件,提取錯誤代碼。
這樣,你重啟后打開腳本,它直接告訴你:“上次死機是因為 nvlddmkm.sys (NVIDIA驅(qū)動) 崩潰”。
這比你自己翻事件查看器快十倍。小結(jié)
“電腦重啟不了”是個老生常談的問題,但大多數(shù)人的處理方式是“拍腦袋重啟”。
今天分享的這套方案,核心不在于代碼有多復(fù)雜,而在于思維轉(zhuǎn)變:把故障當(dāng)數(shù)據(jù)看:日志、進程、資源曲線,都是線索。
工具化:別靠記憶,靠腳本。
性能優(yōu)化前置:在崩潰前發(fā)現(xiàn)瓶頸,比崩潰后重啟更有價值。這個腳本你可以直接拿去用,改改閾值,適配你的環(huán)境。
更重要的是,它幫你建立了一種“可觀測性”的思維。
下次再遇到重啟不了,別慌,打開終端,跑一下 diag.py。
看看日志,看看進程,看看趨勢。
你會發(fā)現(xiàn),問題往往沒那么玄乎,只是你以前沒看見而已。
你公司項目里是怎么處理這種“死機”問題的?是用腳本自動恢復(fù),還是直接重啟了事?歡迎評論,分享你的實戰(zhàn)經(jīng)驗。