电脑重启不了排查实录:3步搞定死机,兼顾性能优化
刚升级完驱动,电脑重启不了?别急着砸键盘。 版本升级后 API 全变了,内核加载逻辑变了,旧配置直接冲突。 这时候硬重启只是治标,我们要做的是定位瓶颈,顺手做个性能优化。
项目目标:构建自动化故障诊断工具
很多兄弟一遇到“电脑重启不了”,第一反应就是拔电源。这没错,但下次呢?还是卡住。 我们需要一个轻量级的脚本,能在系统半死机状态(比如还有网络,或者能进安全模式)下,快速收集关键日志,并给出重启建议。 这个项目目标很明确:
- 快速定位:是硬盘坏了?内存爆了?还是驱动冲突?
- 数据留存:把崩溃前的最后几条日志抓下来,别丢。
- 温和重启:通过系统指令强制重启,避免硬件损伤。
这不是为了炫技,而是为了在下次故障发生时,你能像医生看X光片一样,一眼看出病灶。对于经常跑大型编译、虚拟机集群的开发者来说,这种“重启不了”的代价是巨大的——丢掉的上下文、中断的构建,都是时间成本。
目录结构:极简主义,拒绝过度设计
别搞那种几百个文件的框架,排查工具讲究一个“快”。 我们在项目根目录下只放三个核心文件:
project-root/
├── diag.py # 主诊断脚本
├── config.yaml # 阈值配置(内存、CPU、磁盘IO)
└── logs/ # 自动生成的日志目录└── crash_YYYYMMDD_HHMMSS.log
diag.py 是核心,config.yaml 让你可以根据不同机器调整灵敏度。比如你跑的是高负载服务器,内存阈值设高一点;如果是办公本,设低一点。
logs 目录必须存在,脚本会在启动时自动创建,防止写入失败。
核心代码实现:Python 驱动的诊断逻辑
这里我们用 Python 3.9+ 实现。为什么选 Python?因为跨平台,Windows、Linux、Mac 都能跑,而且库丰富。
核心依赖只有 psutil(监控资源)和 pyyaml(读配置)。
import psutil
import yaml
import os
import time
import subprocess
import logging
from datetime import datetime# 配置日志,输出到控制台和文件
log_file = f"logs/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):"""检查系统健康度返回: (is_healthy: bool, reason: str)"""cpu_percent = psutil.cpu_percent(interval=1)mem_percent = psutil.virtual_memory().percentdisk_io = psutil.disk_io_counters()# 计算磁盘IO使用率 (简化处理,实际需结合读写速度)# 这里仅作为演示,真实场景需监控特定分区io_percent = (disk_io.read_count + disk_io.write_count) % 100 # 伪代码,实际需更复杂逻辑logging.info(f"当前状态 -> CPU: {cpu_percent}%, MEM: {mem_percent}%, IO: {io_percent}%")if cpu_percent > cfg['cpu_threshold']:return False, f"CPU占用过高: {cpu_percent}%"if mem_percent > cfg['mem_threshold']:return False, f"内存占用过高: {mem_percent}%"if io_percent > cfg['disk_io_threshold']:return False, f"磁盘IO瓶颈: {io_percent}%"return True, "System OK"def force_restart(reason):"""执行强制重启在Windows下使用 shutdown /r /t 0在Linux下使用 reboot"""logging.warning(f"触发强制重启: {reason}")# 等待日志刷新time.sleep(2)if os.name == 'nt':# Windows: /r 重启, /t 0 立即, /f 强制关闭应用程序subprocess.call(['shutdown', '/r', '/t', '0', '/f'])else:# Linux/Macsubprocess.call(['reboot'])def main():logging.info("=== 电脑重启不了诊断工具启动 ===")cfg = load_config()# 循环监控,直到系统健康或超时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(f"PID:{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("监控超时,未触发重启,系统可能已恢复或需人工干预")if __name__ == '__main__':main()
逐行解析关键点
psutil.cpu_percent(interval=1):这个interval=1很关键。默认是0,返回的是上次调用的差值,第一次调用永远是0。设为1秒,能拿到真实的瞬时负载。- 日志双写:
handlers里同时加了FileHandler和StreamHandler。文件用来存档,屏幕用来实时看。如果系统卡死,屏幕可能没反应,但文件通常还在写(除非磁盘也挂了)。 - 进程快照:在触发重启前,
top_procs那一段是灵魂。它把当时占用最高的10个进程PID和名字记下来。很多“重启不了”是因为某个后台服务(比如杀毒软件、更新程序)死锁了。有了这个日志,重启后一查PID,立马知道是谁在搞鬼。 - 跨平台重启:
os.name判断系统。Windows 用shutdown命令是最稳定的,比 PowerShell 脚本更底层,不容易被拦截。
运行与测试:模拟“假死”场景
代码写完了,怎么测?总不能真等电脑死机吧? 我们用一个简单的技巧:人为制造资源瓶颈。
准备环境: 安装依赖:
pip install psutil pyyaml编写
config.yaml:cpu_threshold: 50 # 故意设低,方便测试 mem_threshold: 50 disk_io_threshold: 50 restart_timeout: 10制造压力: 打开另一个终端,运行一个简单的死循环脚本,把CPU吃满:
# stress_test.py import time while True:pass或者在 Windows 上打开任务管理器,启动几个高CPU占用的程序。
运行诊断:
python diag.py
预期现象:
- 日志开始滚动,显示 CPU 占用超过 50%。
- 5秒后(因为 sleep(5)),再次检测。
- 如果持续超标,打印 Top 进程列表。
- 执行
shutdown /r /t 0,电脑黑屏,重启。
避坑指南:
- 杀毒软件干扰:有些杀毒软件会拦截
shutdown命令,或者把diag.py标记为可疑。测试前,把脚本目录加入白名单。 - 日志写入失败:如果磁盘IO极高,
logging.FileHandler可能会阻塞。进阶版可以用异步日志,但对于排查工具来说,同步写入更安全,确保数据不丢。
优化扩展:从“重启”到“性能优化”
重启只是止血,我们要的是性能优化。 这个脚本目前只能做“重启”,怎么让它更智能?
增加“优雅退出”逻辑: 在强制重启前,先尝试发送
SIGTERM给高占用进程。如果10秒内没退出,再SIGKILL。# 伪代码 for proc in top_procs:try:proc.terminate()except:pass time.sleep(10) # 检查是否退出,没退出再 kill这样能减少数据丢失风险。
历史趋势分析: 每次运行都把 CPU/MEM 数据追加到一个 CSV 文件。 重启后,用 Python 读这个 CSV,画个折线图。 你会发现:是不是每次在编译到 80% 的时候内存就爆?如果是,那就是代码内存泄漏,或者编译器配置问题。 这才是性能优化的核心:数据驱动,而不是玄学调参。
集成到 CI/CD: 在自动化测试服务器上,把这个脚本跑起来。 如果服务器重启了,自动把
logs/crash_*.log打包,通过邮件或企业微信发给开发者。 我见过一个团队,靠这个功能,在一个周内定位了三个导致服务器死机的 Java 线程死锁问题。Stack Overflow 上有很多关于OutOfMemoryError的讨论,但光看堆栈不够,要看系统级的资源曲线。Windows 事件日志集成: 在 Windows 上,可以用
win32eventlog库读取系统事件日志。 有些“重启不了”是因为内核蓝屏(BSOD),但重启后蓝屏信息丢了。 脚本可以在启动时,读取最近一次的 BSOD dump 文件,提取错误代码。 这样,你重启后打开脚本,它直接告诉你:“上次死机是因为nvlddmkm.sys(NVIDIA驱动) 崩溃”。 这比你自己翻事件查看器快十倍。
小结
“电脑重启不了”是个老生常谈的问题,但大多数人的处理方式是“拍脑袋重启”。 今天分享的这套方案,核心不在于代码有多复杂,而在于思维转变:
- 把故障当数据看:日志、进程、资源曲线,都是线索。
- 工具化:别靠记忆,靠脚本。
- 性能优化前置:在崩溃前发现瓶颈,比崩溃后重启更有价值。
这个脚本你可以直接拿去用,改改阈值,适配你的环境。
更重要的是,它帮你建立了一种“可观测性”的思维。
下次再遇到重启不了,别慌,打开终端,跑一下 diag.py。
看看日志,看看进程,看看趋势。
你会发现,问题往往没那么玄乎,只是你以前没看见而已。
你公司项目里是怎么处理这种“死机”问题的?是用脚本自动恢复,还是直接重启了事?欢迎评论,分享你的实战经验。