内核panic是Linux系统中最严重的故障形态,一旦触发,整个系统的内核停止工作,所有进程无法继续调度,只能等待重启。对于运维和开发人员来说,面对panic最头疼的不是重启本身,而是找不到原因——系统重启之后现场就没了,日志可能只留下半截。这篇文章从panic的触发机制讲起,详细分析常见原因,并给出一套可落地的排查方案,重点是利用kdump抓取崩溃转储并用crash工具做分析。

一、内核panic到底是什么,和oops有什么区别
很多人把oops和panic混为一谈,实际上两者有本质区别。oops是内核在运行中发现了一个严重错误,比如非法内存访问,内核会打印出错时的寄存器状态、调用堆栈,然后终止当前进程。如果出错发生在进程上下文,系统通常还能继续运行,只是那个进程被杀掉了。
panic则不同,它代表内核认为自己已经无法继续安全运行。典型的情况包括:oops发生在中断上下文或内核线程中、关键数据结构被破坏、init进程意外退出、内核检测到数据一致性无法保证等。触发panic时,内核会调用panic()函数,打印崩溃信息,然后根据panic=参数决定是立即挂起还是等待一段时间后自动重启。
从排查角度看,两者的价值也不同。oops的堆栈信息往往完整保留在dmesg里,重启后也能从/var/log/messages或journalctl中找到;而panic发生时系统已经失去响应,日志可能来不及落盘,这正是需要kdump这类专门机制的原因。理解这个区别,能帮你在故障报告中快速判断问题等级和取证方式。
二、常见的panic触发原因有哪些
第一类是内存问题,包括非法地址访问、空指针解引用、SLAB损坏等。这类问题多由驱动代码缺陷引起,比如驱动的中断处理函数访问了已经释放的内存,就会在中断上下文触发oops,进而升级为panic。排查时堆栈里如果出现具体驱动模块的符号名,基本可以直接锁定嫌疑人。
第二类是硬件故障。内存条坏块、ECC报错超出阈值、CPU过热、PCIe设备异常都可能触发内核的不可恢复错误处理路径。特别是MCE(Machine Check Exception),一旦硬件报告不可纠正的错误,内核通常直接panic。这类问题的特点是堆栈指向mce相关函数,需要结合mcelog或rasdaemon的记录来确认。
第三类是存储与文件系统问题。根文件系统所在磁盘故障、EXT4或XFS检测到元数据不一致时,可能触发kernel panic - not syncing: attempted to kill init或文件系统相关的严重错误。此外,initramfs损坏、磁盘驱动加载失败导致根分区挂不上,也是启动阶段panic的高频原因,这类问题往往在系统引导时就表现出来。
第四类是人为配置错误,比如内核参数写错、/etc/fstab中引用了不存在的设备、升级内核后模块不匹配等。虽然技术含量不高,但在实际故障中占比不小,排查时不要一上来就怀疑深层Bug,先检查最近的变更记录往往事半功倍。
三、用kdump和crash工具抓取并分析崩溃现场
kdump的工作原理是在系统启动时预留一段内存(由crashkernel=参数指定),正常运行时这段内存不被使用。当panic发生时,内核借助kexec机制快速启动一个预加载的捕获内核,这个新内核启动后把崩溃时的内存镜像导出为vmcore文件,供事后分析。相比等待磁盘同步写日志,这种方式可靠得多。
配置的第一步是修改内核启动参数,预留捕获内核所需的内存:
# 编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 中追加 # 256M大小对多数场景够用,ARM大内存机器可适当加大 GRUB_CMDLINE_LINUX="crashkernel=256M" # 重新生成grub配置并重启 grub2-mkconfig -o /boot/grub2/grub.cfg reboot # 安装kdump工具并启动服务 yum install kexec-tools crash -y systemctl enable --now kdump.service # 验证kdump是否就绪 kdumpctl status cat /sys/kernel/kexec_crash_size
第二步是确认崩溃后vmcore的存放路径。CentOS和RHEL默认写到本地/var/crash/目录,可以通过修改/etc/kdump.conf调整,比如用NFS挂载到远程服务器,避免本机磁盘本身就故障导致转储丢失。配置完成后,可以手动触发一次测试:
# 触发panic测试kdump是否工作,确认前先保存业务数据 echo c > /proc/sysrq-trigger # 系统崩溃重启后,检查转储文件是否生成 ls -lh /var/crash/127.0.0.1-*/ # 一般包含两个文件:vmcore(完整内存镜像)和vmcore-dmesg.txt(崩溃日志)
拿到vmcore后,用crash工具打开分析。启动时需要指定崩溃内核的vmlinux调试符号文件和vmcore路径,进入交互环境后,几个常用命令能覆盖大部分分析场景:
# 打开转储文件,vmlinux需与崩溃内核版本严格匹配
crash /usr/lib/debug/lib/modules/3.10.0-1160.el7.x86_64/vmlinux \
/var/crash/127.0.0.1-*/vmcore
# 常用分析命令
crash> log # 查看崩溃前的内核日志,定位panic原因
crash> bt # 查看崩溃时的调用堆栈,最核心的命令
crash> ps # 查看崩溃瞬间各进程状态
crash> vm <pid> # 查看指定进程的虚拟内存布局
crash> dev -l # 查看崩溃时的中断和设备信息
crash> mod # 列出已加载模块,配合堆栈符号定位问题驱动
bt命令输出的堆栈是定位问题的关键。从上往下看,最顶层的函数通常是出错点,比如看到igb_xmit_frame之类的驱动函数,就可以把怀疑对象锁定到对应网卡驱动。如果堆栈中出现panic、do_exit、oops_end这类函数,说明中间经历了一次oops升级,还需要结合log命令输出的第一现场信息往前追溯。
四、没有kdump时如何应对以及预防措施
如果事发前没有配置kdump,也不是完全没办法。首先检查netconsole或串口日志有没有配置——通过串口线或网络把内核日志实时发到另一台机器,是数据中心环境的标准做法。其次,重启后立即执行journalctl -b -1 -k查看上一次启动的内核日志,部分信息可能已经持久化。另外,IPMI的SEL日志、服务器的硬件诊断日志也值得检查,用于排除硬件层面的问题。
预防层面有几点建议。一是生产环境默认开启kdump,并把转储路径指向独立存储;二是开启panic=10之类的参数让系统自动重启,减少业务中断时间,但同时要确保kdump转储完成后再重启,这由kdump.conf中的default选项控制;三是对频繁崩溃的机器,开启kernel.softlockup_panic等检测项,把隐性挂死也纳入panic捕获范围,反而更容易留全现场;四是及时更新内核和驱动补丁,很多社区已修复的panic问题,不需要自己重新踩坑。
最后补充一点经验:分析panic时先看崩溃频率和规律。偶发一次和每几小时必现的处理策略完全不同,前者优先怀疑硬件和环境影响,后者更可能是软件 Bug。掌握kdump加crash这套组合拳,绝大多数panic都能找到明确根因,告别只能重启碰运气的被动局面。