我的主力工作机是一台跑 Ubuntu 24.04 LTS 的台式工作站,内核版本属于 6.8 系列,日常主要是编译、跑虚拟机、偶尔处理点视频。就在我快把系统调教到“懒得再碰”的时候,它连着给了我两次一模一样的下马威:屏幕毫无征兆地定格,键盘鼠标完全失去响应,所有风扇像突然打了鸡血一样狂转,然后屏幕上滚出一屏传统艺能——Kernel Panic。
第一次出现时,我下意识地把它归类为“偶发事件”,毕竟 Ubuntu 24.04 刚装完那阵子各种驱动小毛病也不少。重启之后简单看了两眼日志,顺手把 BIOS 刷到最新、更新了几个固件包,就继续干活了。直到几周后同一个场景第二次原样上演,我才意识到,这根本不是运气问题。如果不去把这个根子挖出来,它大概率还会第三次、第四次找上门。
这篇文章就是我第二次排查并最终“永久性解决”这份 Kernel Panic 的完整记录。没有玄学,也没有“重装系统大法”这种权宜之计,我会把整个排查链路、中间走过的弯路、最后锁定的根因以及验证过程都摊开写清楚。如果你也正被 Ubuntu 24.04 或者其他 Linux 发行版的随机内核崩溃折磨,这篇流程应该能帮你省下大量试错时间。
1. 事故重现与第一次“假解决”复盘
先说说两次事故的具体场景和我的处理方式,这一段很关键,因为第二次能顺利定位到根因,很大程度上得益于第一次“假解决”留下的对比样本。
1.1 第一次现场:当机发生在任务切换瞬间
第一次 panic 发生在 11 月中旬,当时我开着 Firefox 看文档,虚拟机里跑着一个编译任务,宿主机桌面上还挂着 VS Code。一切都很正常,没有高负载、没有异常高温,结果我从虚拟机窗口切回宿主机桌面的那一瞬间,屏幕整个冻住了。
当时我第一反应是 NVIDIA 显卡驱动又出幺蛾子,因为 24.04 默认的 open 内核模块和闭源驱动在切换窗口、DPMS 唤醒时偶尔会有 bug。我按了几次 Caps Lock 键,键盘灯毫无反应,确认系统是彻底死锁而不是单纯的桌面卡死。等了大约一分钟,屏幕上才慢悠悠滚出几行 Kernel Panic 信息,其中最显眼的就是 Kernel panic - not syncing: Fatal exception。
由于没有配置串口控制台或者 kdump,panic 出现时现场信息其实丢了很多。所以我当时能做的只是长按电源键强制重启,然后赶紧进系统查日志。但这里我犯了一个很多新手都会犯的错:只看了当前会话的日志,却没有意识到实际上 panic 之前的信息根本没有完整落盘。我查了 journalctl -p err -b,只看到一些 GPU 相关的 minor error,并没有找到能一锤定音的证据。
1.2 处理过程的第一个失误:只做了外围升级
第一轮排查我做了什么?说来惭愧,其实都是些“看起来在解决问题、实际上只是碰运气”的动作:
- 把主板 BIOS 从 F3 版本刷到了 F6 最新版;
- 用
sudo apt update && sudo apt full-upgrade把内核从 6.8.0-38 更新到了当时的 6.8.0-45; - 顺手重装了 NVIDIA 驱动,确保 DKMS 模块和当前内核版本对齐;
- 用
sudo fsck -f /dev/nvme0n1p2检查了根分区文件系统。
做完这一套之后,系统确实安静了大约三周。这段时间里我甚至有点沾沾自喜,觉得那次 panic 就是老 BIOS 和内核之间不兼容导致的偶发事件。直到第二次 panic 在几乎完全相同的使用场景下再次出现,我才意识到一个问题:上一次的“解决方案”根本没解决任何东西,我只是在系统周边敲敲打打,从来没找到那个真正的病根。
这个教训值得先记下来:Linux 内核崩溃的排查,永远不要用“我更新了一堆东西所以它应该好了”来安慰自己。 如果第一阶段不能给出明确的根因结论,那这次“解决”百分之百是假解决。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第二次排查:从日志拉取到范围收敛
第二次 panic 发生之后,我用了一个和第一次完全不同的姿势来处理问题。这次不再是“猜哪个组件坏了”,而是先尽力保留现场、扩大证据收集范围,再一步步做减法。
2.1 panic 后的第一件事:抢救上次启动的日志
系统强制重启之后,Ubuntu 默认的 systemd-journald 实际上不会保存本次 panic 时刻的日志到磁盘,因为内核已经卡死了,用户态服务根本来不及响应写盘请求。但好消息是,panic 之前一段时间内的日志通常是完整的,尤其是一些反复出现的硬件级错误,很早之前就可能已经在疯狂刷记录了。
我做的第一件事就是查看上一次开机(即 panic 对应的那一次 boot)里有没有可以交叉验证的异常信息:
bash复制# 列出历史启动记录,找到上一次 boot 的 ID
journalctl --list-boots
# 查看上一次启动的内核日志,过滤关键错误
sudo journalctl -k -b -1 --no-pager | grep -Ei 'panic|oops|bug|error|fail|timeout'
如果你在 Ubuntu 24.04 上执行 journalctl --list-boots 发现只有一个 boot,也就是当前这次,那是因为你的日志目录没有持久化。早期版本的 Ubuntu 默认把日志存在内存里的 /run/log/journal,重启后即丢。你可以先执行下面这条命令开启持久化,再等下一次复现时才有日志可查:
bash复制sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
我在二次排查时发现,日志里确实存在大量 PCIe AER 报错,比如 pcieport 0000:00:1c.1: AER: PCIe Bus Error、BAR 13: failed to assign [mem size 0x00200000] 之类,看起来非常像 PCIe 链路或地址分配出问题。如果你遇到这种关键词,第一反应通常会怀疑显卡或 NVMe SSD,但实际上,PCIe 报错经常是内存控制器出问题之后的“连带受害者”,并不是元凶。这个我需要单独拿出来讲清楚,因为它极容易带偏整个排查方向。
2.2 常见报错关键词的误导性分析
判断内核 panic 根因,最难的不是看到报错,而是分辨哪些是“因”、哪些是“果”。我在这个阶段整理过一个简单的对照表,用于快速评估日志里各类报错的可信度:
| 日志关键词 | 第一直觉嫌疑 | 实际排查优先级建议 |
|---|---|---|
Kernel panic - not syncing: Fatal exception |
无指向性 | 优先排查内存、供电、驱动冲突 |
Hardware Error: PCIe Bus Error |
显卡/PCIe 设备 | 除了排查 PCIe 设备本身,也要评估内存控制器信号完整性 |
BAR: failed to assign |
BIOS 地址分配问题 | 先升级 BIOS,无效再去考虑设备故障 |
BUG: unable to handle page fault |
某个驱动模块 | 定位到模块后,还要注意模块访问的内存是否来自错误的内存页 |
Out of memory and no killable processes |
内存耗尽 | 通常是真正物理内存故障或驱动内存泄漏 |
watchdog: BUG: soft lockup |
CPU 频率、ACPI | 也可能是中断被异常 STI 阻塞,和内存错误路径相关 |
我第二次排查的日志里,上述表中间的三种错误其实都有出现,但如果只看表面关键词,很容易把所有责任都推到 BIOS 的 PCIe BAR 分配逻辑上。事实上,BAR 13: failed to assign 这类错误通常发生在系统启动阶段的内存映射建立过程中,如果伴随其他内存访问异常,就必须把内存故障作为贯穿性假设。
2.3 从“软件怀疑”切换到“硬件验证”
在连续两次出现同样的 panic 之后,我给自己定了一条排查原则:先排除硬件,再回过来怀疑软件。 软件层面的内核模块 bug、驱动兼容性问题当然存在,但如果同样的 panic 在几乎一样的负载场景下重复发生,且日志里没有一个稳定可复现的驱动调用栈,那么硬件的优先级必须提前。
怎么排除硬件?不是拆开机箱看主板电容有没有鼓包,而是做两层压力验证:
- 内存层:用 memtest86+ 这类独立于操作系统运行的底层内存测试工具;
- 整机层:在系统内用
stressapptest、memtester对内存控制器做高强度读写,同时跑 CPU 和 GPU 负载,观察是否合成出类似的 panic。
后面的事实证明,这套“先硬件后软件”的思路帮我直接就找到了真正的病根。内存测试没有跑满一整轮,错误就已经藏不住了。
3. 识别并隔离真正的根因:内存子系统的隐性故障
如果你遇到同样的问题,我建议下面的“内核内存压力测试 + BIOS 降频验证”可以作为复现内核 panic 的标配套餐。不需要准备多专业的设备,但每一步都要做记录,方便对比。
3.1 准备一个独立于系统的 Memtest86+ 启动盘
为什么不直接在系统里跑内存测试?因为系统自身运行时会占用大量内存,测试工具只能用剩余物理内存做读写验证,覆盖面积大打折扣。更重要的是,如果内核本身已经因为内存错误而变得不稳定,在系统内做测试可能刚跑起来系统就先崩了,根本测不出结果。
我建议的制作方案是:
bash复制# 先从 memtest86+ 官网下载 USB image,然后确认 U 盘设备名
lsblk
# 写入 U 盘,千万不要把设备名写错成自己的系统盘
sudo dd if=memtest86plus-7.00.usb.img of=/dev/sdb bs=4M status=progress
sync
如果你机器开启了 UEFI Secure Boot,部分主板的 Secure Boot 策略会阻止独立 memtest86+ 引导进入。遇到这种情况,可以暂时进 BIOS 把 Secure Boot 关掉,测完内存之后再恢复。我自己的主板开启 Secure Boot 时没办法引导 Memtest86+,关闭后一切正常。
Memtest86+ 启动后默认会跑 4 轮全内存测试。请记住:不要跑了一轮没报错就觉得内存没问题。 很多内存条的位翻转故障要跑到第二轮、第三轮才会稳定复现,尤其是在 Test 6 和 Test 7 那种针对地址线和缓存一致性的压力测试环节。
3.2 事故元凶浮出水面:Memtest86+ 第二轮报错
我的实际测试结果如下:第一轮 12 个测试项目全部通过,系统显示 Pass 1, 0 errors。但从第二轮 Test 6 开始,屏幕下方开始不断报错,错误类型是 Word 0x00000000_00000100, Expected 0x00000000_00000000 这一类典型的“写入后读出不一致”错误。更耐人寻味的是,错误地址集中在物理内存 16GB 到 32GB 区间,也就是两条内存中的第二条所在的通道。
这时候,之前日志里的 PCIe AER 报错就完全解释通了:内存控制器管理着所有系统内存的路线,当某根内存条上出现了数据位翻转,CPU 在访问由内存控制器桥接的 PCIe 设备(包括 NVIDIA GPU 和 NVMe SSD)时也会产生偶发的故障信号。也就是说,那些所谓显卡报错其实是内存错误的表亲,不是亲爹。
我当时用的是 DDR4 3200MHz 的两条 16GB 内存,购买时间大概两年多。看到报错集中在第二条内存条上,我先没有急着换硬件,而是做了一个对照实验:把两条内存互换插槽,再次跑 Memtest86+。结果报错依然集中在原先那条物理内存条对应的插槽上,而不是原插槽。这个实验进一步排除了主板插槽或 CPU 内存控制器故障的可能。
3.3 BIOS 层面的“临时止痛药”:降频后确实不报错了
等确认是某一条内存条本身有问题之后,我本来可以直接下单换内存,但为了搞清楚“如果不换,单纯靠 BIOS 降频能不能把这个问题压住”,我又做了另一个实验:进 BIOS 把内存频率从 3200MHz 手动降到 2666MHz,并且把 XMP/EXPO 配置关闭。重启后再次跑 Memtest86+,让我没想到的是,这一轮六遍测试全部通过。
这个现象在维修圈里很常见:内存颗粒的时序劣化或虚焊初期,高频运行下的信号时钟裕量不足,就会偶发读写错误;把频率降下来,相当于放宽了每个时钟周期内信号稳定读取的时间窗口,错误自然被掩盖。
但请注意,这只是一颗止痛药,不是治疗方案。降频后的系统在大负载下也许三五天不报错,但并不能改变内存颗粒物理老化的事实。更重要的是,我这次的目标是“永久性解决”,而不是“让下一轮 panic 来得更晚一些”。
3.4 为何“更换内存条”而不是“长期降频运行”
这里我需要和读者坦诚说一点经验:Linux 系统遇到随机 Kernel Panic 时,最容易被忽略的硬件根因恰恰是内存。因为内存颗粒的老化不像机械硬盘那样有 smart 自我报告机制,普通用户很难从系统内直接感知。
长期降频运行虽然能掩盖大部分问题,但存在三个隐藏风险:
- 降低内存频率会同步降低内存控制器的时钟,影响整机大数据量吞吐性能,尤其对虚拟机、编译任务影响明显;
- 老化内存颗粒的故障可能随时间进一步加重,今天 3200MHz 出错,明年可能 2666MHz 也开始出错;
- 内存错误一旦发生在关键内核代码页或文件系统缓存页,造成的损坏可能远比一次 panic 更隐蔽,例如数据写入时出现静默损坏,这种损坏不会让系统崩溃,但会让文件静默损坏。
所以在更换内存条和降频运行之间,我果断选择了前者。一条全新的同规格内存条价格并不离谱,但一份被静默损坏的源码或论文就是无价之宝了。
4. 永久性解决步骤与稳定性验证
如果你也走到了“内存条疑似故障”的环节,下面这套更换和验证流程基本可以完全照抄。重点是别换完条子就急着宣布胜利,一定要在替换前后都做完整的压力验证,形成可对比的结论。
4.1 更换硬件并清理内存插槽与金手指
拆机之前先把机器彻底断电,拔掉电源线后长按开机键三秒放掉主板余电。然后佩戴防静电手环或先触摸一下机箱金属框架释放静电。这里有个小细节:内存插槽两侧的卡扣要同时向外扳开,取出内存条时不要左右摇晃,直接垂直向上拔出,避免损坏插槽内部的金属弹片。
由于是二条内存只挂了其中一条,我先只替换故障的那条,保留正常的那条。但如果你预算允许,我会更建议把两条一起淘汰,换成两条全新的同一批次内存,保证颗粒一致性。自己家里没有颗粒一致性测试设备,新旧混插只会增加未来出现奇怪问题的概率。我最终选择了同品牌同规格的两条 16GB 新内存。
换上新内存后,先不要马上盖上机箱侧板,也先不要进系统。直接开机,进入 BIOS 确认内存被正确识别为运行在 3200MHz,并且 XMP 默认开启。如果你之前为了验证问题手动关过 XMP,记得在换完新条子之后重新打开,否则内存会降频到保守的 2133MHz 或 2400MHz,白白浪费性能。
4.2 再次运行 Memtest86+,验证替换后的稳定性
这台机器在使用新内存后,我重新进入了 Memtest86+ 做了整整四轮完整测试,测试时间接近十个小时。四轮测试结束后,界面右下角依然是 Pass 4, 0 errors。这已经和故障条子测出的报错形成了鲜明对比。
如果你不想跑到四轮这么久,通常跑到两轮以上 0 errors 就已经相当可信了。实际经验中,我见过少数需要跑到第三轮才报错的内存条,但那是极端案例。稳妥起见,你可以在跑完两轮后直接跳到系统内压力测试,因为系统内测试更接近真实使用负载,更容易暴露整机配合问题。
4.3 系统内压力测试:stressapptest 高负载反复“锤打”内存
执行完启动盘层级的内存测试后,建议再做一次系统内的压力验证。工具选择方面,stressapptest 是 Google 退出的内存压力工具,专门用来在高负载环境下检测内存控制器和数据路径上的硬件错误,如果它都能顶住,那至少证明系统在正常使用中很难因为内存本身再出 panic。
bash复制sudo apt install stressapptest
# -M 指定内存总量,-s 指定测试秒数,-m 指定并发线程数
sudo stressapptest -M 30000 -s 7200 -m 8
我跑了一次两小时的 stressapptest,同时保持一个虚拟机开着编译任务,让它和内存压力测试同时进行。跑完一切正常,没有重现之前的 freeze 或 panic。这时候我才有信心说:问题的根子被挖掉了。
4.4 “永久性解决”不是玄学,而是证据链完整
很多社区帖子喜欢用“我重装系统后再也没出过问题”来收尾,这种表述其实非常不严谨。因为重装系统会同时改变内核版本、驱动状态、运行时长、负载模式等多个变量,你没出问题可能只是运气。
真正的永久性解决,应该能回答下面三个问题:
- 故障现象是否稳定复现?我的案例中,Memtest86+ 第二轮稳定报错,属于可复现故障。
- 修复动作是否直接作用于根因?我替换了被检测出错误的内存条,是物理层面的修复,而不是用配置绕开问题。
- 修复后是否经受了足够的验证?我做了启动盘内存测试加系统内压力测试的双保险,验证强度远高于日常使用。
在替换内存并完成以上验证之后,我的这台主机到现在已经连续运行了几周没有再出现过任何内核崩溃。当然,这世上没有绝对的“永久”,但至少这次修复建立在了可追溯的证据链上,而不是听天由命。
5. 内核 Kernel Panic 通用排查手册与长期防护建议
最后这部分,我想把这次排查中沉淀下来的通用方法论单独整理出来。以后你不管面对什么发行版、什么硬件,遇到随机内核崩溃都可以按这个顺序走,避免像我第一次那样原地打转。
5.1 遇到 Kernel Panic 后的 60 分钟快速行动清单
panic 发生后最怕两件事:一是慌张重启后没有保留日志,二是过早陷入具体驱动的细节里。以下是我梳理的行动顺序,按优先级排列:
- 不要马上强行重启,先掏出手机拍下屏幕上的 panic 信息,尤其是其中
RIP:后面跟着的函数地址和模块名称、以及Call Trace最后几行,它们是最直接的现场线索。 - 强制重启后进入系统,立刻确认
journalctl持久化日志目录是否存在,如果不存在马上开启。同时把journalctl -b -1中 error 级别以上的日志导出备用。 - 查看是否存在硬件级错误汇总工具的信息,如
sudo dmesg -T | grep -Ei 'error|fail|hardware'。如果有/var/crash目录,检查里面是否生成了 crash dump。 - 回忆故障发生的前置条件:是在高负载、低负载、休眠唤醒、插拔外设之后,还是无规律出现?这个前置条件会直接影响排查方向。
- 根据优先级先做一轮硬件自检,重点是内存、硬盘的 SMART 信息、CPU 温度、电源供电是否稳定。
- 不要急着重装系统或编译新内核,除非你能确认问题只发生在新特性相关路径上。
记住,Kernel Panic 时屏幕上最显眼的 Kernel panic - not syncing: Fatal exception 通常毫无排查价值,它只是内核最后抛出的一张“病危通知书”,真正需要诊断的是病因。而病因往往要依赖你提前配置好日志收集设施才能锁定。
5.2 长期建议开启的两项内核调试防护
如果你不想在每次 panic 之后都靠“回忆现场”来排查,那就花十分钟给系统装上一个“黑匣子”。第一项是 kdump/crashdump 机制,它能让内核在崩溃瞬间把内存转储写入磁盘,供你之后用 crash 工具离线分析。但在普通工作站上配置 kdump 涉及预留内存、kdump-tools 服务等一堆环节,对新手并不友好,我自己也只在服务器上开启。
更轻量也更推荐给普通用户的做法是:确保内核日志通过串口或网络传输到另一台机器。做法如下,但前提是你有两台设备,一台 Ubuntu 作为接收端:
bash复制# 接收端,启动 netconsole 并指定本地端口
sudo modprobe netconsole netconsole=@/192.168.1.100,6666@192.168.1.200/enp3s0
如果调试对象就是你现在正在使用的这台机器,那我没有特别轻量的一行命令可以保护你完全不留死角。比较现实的替代方案是:充分利用 Ubuntu 24.04 的 Apport 错误上报机制,让内核 oops 发生时自动写入 /var/crash,至少能留下一点痕迹。
bash复制cat /proc/sys/kernel/panic_on_oops
在我这台机器上,默认值是 1,即遇到 oops 也会直接 panic。如果你希望 oops 发生后只杀掉出问题的进程而不是整个系统崩溃,可以考虑临时改为 0。不过这只适合测试环境,生产工作站保持默认更安全。
5.3 一些容易被忽视的硬件排查细节
内存条的老化不是唯一能制造“玄学 Kernel Panic”的硬件原因。根据我个人维修经验,以下几个坑都容易伪装成内核或驱动问题,值得你在排查时一起检查:
- 电源供电老化:大功率显卡瞬时功耗冲击会让电源输出电压跌落,表现为高负载下随机死机。这种问题普通软件日志几乎看不出来,只能通过替换电源做排除法。
- NVMe SSD 过热:很多主板的 M.2 插槽没有独立散热片,SSD 在高负载下温度冲到 80℃ 以上后会出现 IO 超时,进而引发
nvme驱动报错。 - 主板 PCIe 插槽接触不良:显卡、声卡、采集卡等 PCIe 设备在机箱共振或热胀冷缩后,金手指可能出现氧化,导致链路降速或 AER 报错。
- CPU 散热器压力不均:如果散热器一边螺丝没拧紧,CPU 可能只是轻微弯曲,造成部分内存控制器触点接触不良,这个故障极其隐蔽,Memtest86+ 都可能测不出来。
更换内存后我发现之前有一个不太起眼的症状也消失了:NVIDIA 驱动偶尔在 Xorg 日志里报 Xid 79 错误。之前我一直以为是显卡驱动或者 GPU 硬件问题,查了很久都没法复现。现在回头看,极大概率也是内存控制器故障导致的显存同步错误。这也算是一个意外收获。
5.4 最后分享一条很实用的排障经验
如果你以前遇到过 Linux 系统随机死机、重启后日志一片空白的问题,我强烈建议你抽半小时把 /var/log/journal 持久化开启,并且在 BIOS 里打开内存的完整自检选项。
很多人喜欢追求开机速度,把 BIOS 里的“快速启动”和“内存快速训练”打开,这能让系统在 5 秒内进入桌面,但代价是跳过了一部分硬件自检。对于内存健康度本来就不稳定的机器,这个设置可能会让系统进入一个带隐患运行的状态,直到某个特定数据模式触发错误。内存条故障不会因为你没测它就不存在,它只会在你最不希望出问题的时候给你上一课。
