ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

kdump内核崩溃转储实战:从配置到vmcore分析一次讲清

kdump内核崩溃转储实战:从配置到vmcore分析一次讲清 手底下的服务器半夜又悄悄panic了早上过来一看机器已经重启完成日志里干干净净连个崩溃现场都没留下——碰到这种情况你第一反应是什么反正我的第一反应是早知道早点上kdump。kdump是Linux内核崩溃转储机制简单说当主内核因为严重错误而陷入瘫痪时一个预先加载的捕获内核会快速接管把主内核当时的内存快照vmcore保存下来供我们从crash工具里复盘现场。很多运维工程师和Linux驱动开发者排查宕机问题查得头破血流就是因为当初没花一个小时把kdump配置好。这篇文章我按自己平时干活的路子把kdump的原理、内存预留、配置实操、故障排查和vmcore分析串一遍适合正在折腾服务器稳定性、写内核模块、或者被线上重启问题折磨得想换行的朋友。1. 先搞明白 kdump 在崩溃现场扮演什么角色1.1 捕获内核给“尸体”拍快照的黑匣子内核panic那一刻整个系统的CPU、内存、设备状态都处于一个高度不可信的状态。普通日志、串口输出、网卡发包这些动作本身就需要内核正常运转崩溃时根本执行不了。kdump的思路是平时用kexec把一个专门用于“收尸”的小内核提前加载进内存里但并不启动它当主内核崩溃时这个捕获内核被快速拉起接管系统把主内核占用的那段物理内存原样保存成文件也就是vmcore。这套机制很像飞机上的黑匣子——飞机都解体了黑匣子还能靠独立电源记录最后的飞行数据。从原理上说kdump依赖两个内核特性kexec系统调用和内核崩溃时的回调路径。kexec允许一个内核在不经过BIOS/UEFI的情况下直接覆盖启动另一个内核省去了固件初始化的时间而主内核在panic时会通过注册好的kexec_crash内核映像跳转到捕获内核入口。捕获内核启动后会把自己“隔离”在专门预留的内存区域中不去碰主内核的崩溃现场然后利用内部的makedumpfile机制把主内核的内存页有选择地压缩并写入磁盘。这一步非常关键捕获内核本身也是内核它也要分配内存、建立页表、加载驱动如果和主内核混用同一片内存等于破坏案发现场。1.2 几个名词一次讲清vmcore、crashkernel、捕获内核刚接触kdump的人最容易弄混这几个词。从我给团队培训时的经验看用下面这张对照表理解最快术语作用类比主内核正在运行的正常内核崩溃后成为“被检查对象”出事的飞机捕获内核崩溃时启动的瘦身内核负责保存现场黑匣子crashkernel内核启动参数指定为捕获内核预留的内存区域黑匣子的独立电源vmcore主内核内存快照的转储文件可能很大黑匣子记录数据kexec快速启动另一个内核的系统调用/工具快速换班的机制crash工具离线分析vmcore的调试器事故调查员我在排查问题时发现很多人把crashkernel当成kdump本身把vmcore当成一个普通日志文件去看结果自然无从下手。vmcore是二进制文件和/etc/messages根本不是一个物种必须用crash或gdb配合调试符号来解析。1.3 什么时候值得上 kdump什么时候别硬上不是所有“机器挂了”都需要kdump。如果是纯用户态进程崩溃比如Java应用抛出OutOfMemoryError、C程序段错误用coredump或systemd-coredump就够了抓的是用户态进程的内存镜像内核本身还活着。如果只是内存不足触发系统OOM杀手那优先排查cgroup限制、内存泄漏、业务峰值再考虑kdump是否可能因为系统根本无法分配内存而失败。kdump真正解决的是内核态崩溃内核空指针解引用、内核栈溢出、驱动崩溃、硬件异常触发MCE、RCU stall导致系统假死等。需要上kdump的典型场景我总结了一下生产服务器频繁无故重启或hang死、开发和测试自定义内核模块。2. crashkernel 内存预留配置最容易翻车的一步2.1 不同发行版的安装与配置入口kdump的实现和配置跟发行版绑定得很紧但核心思路一致。Debian/Ubuntu系用的是kdump-toolsRHEL/CentOS/Fedora系用的是kexec-toolsopenSUSE也走kdump套餐。我平时主要混迹Ubuntu Server和CentOS Stream两边都不能用同一套配置路径。Debian系装完后靠/etc/default/kdump-tools控制RHEL系靠/etc/kdump.conf和/etc/sysconfig/kdump。你要是跨发行版做迁移别把配置文件直接拷过去会出很多莫名其妙的错。安装命令其实很简单但有一个很容易被忽略的组件kernel debuginfo包。没有它crash命令打开vmcore时会报缺少vmlinux符号文件根本没法看调用栈。Ubuntu上要额外开ddebs源安装linux-image-$(uname -r)-dbgsymRHEL上要装kernel-debuginfo-$(uname -r)。这个包体积动不动几百MB但一定要装省不掉的。2.2 crashkernel 取值怎么算才合理crashkernel是内核启动参数它的取值直接决定捕获内核能不能正常启动。太小捕获内核起不来或者转储时OOM太大白白浪费生产内存。我见过不少生产环境管理员随手写了个crashkernel256M服务器变内存紧张就开始互相甩锅。crashkernel的取值逻辑要拆开看捕获内核本身运行需要内存initramfs解压需要内存建立崩溃内存页的页表需要内存makedumpfile在写vmcore时还要一个缓冲区域。如果把捕获内核的kernel image、initramfs占用的内存、动态分配结构都算上x86_64架构下最小能跑通的底线大概在256M附近但那是理论值。我自己的经验是总内存4G以下给256M4G到16G给512M16G以上给768M或1G比较稳。RHEL官方文档里建议crashkernelauto由内核自动计算但这个“auto”在部分老内核和云环境中表现并不稳定我看到过auto算出来过小导致捕获内核启动失败的案例。如果你不想纠结可以直接按内存规模选一个区间。想要更精细可以用crashkernel的range语法比如crashkernel512M-2G:256M,2G-8G:384M,8G-32G:512M,32G-64G:768M,64G-:1G意思是内存小于512M不预留512M到2G预留256M以此类推。这个语法用起来很方便它会在指定内存范围的起始处预留内存。目标是找到一个安全的平衡点。2.3 让内核参数真正生效grub 与 initramfs 那点事改完crashkernel参数后很多人直接重启发现grub里根本没有这个参数原因是没更新grub配置。Debian系改/etc/default/grub里的GRUB_CMDLINE_LINUX改完跑update-grubRHEL系改/etc/default/grub后用grub2-mkconfig -o /boot/grub2/grub.cfg。这一步漏掉的人非常多因为改文件本身不报错系统重启也不会失败但你就是发现/proc/cmdline里没有crashkernel然后白折腾半天。还有initramfs的问题。kdump服务启动时要加载捕获内核的initramfs而捕获内核的网络、磁盘驱动、文件系统支持都要靠这个initramfs提供。如果你用了LVM、RAID、加密磁盘、网络根文件系统必须用dracut --kdump rebuild重新生成捕获内核的initramfs否则捕获内核启动时根本挂不上根文件系统vmcore写不出去。这块属于“配置时没感觉、崩溃时才后悔”的高危区。启动后检查参数效果用cat /proc/cmdline看有没有crashkernel再看/sys/kernel/kexec_crash_loaded是不是1。2.4 记得验证预留区域是否真的可用即使内核参数生效也不代表捕获内核一定加载成功。我踩过一个大坑某些BIOS固件保留内存区域和crashkernel指定区域冲突导致kexec加载捕获内核时报Cannot allocate memory。这种情况下需要给crashkernel指定起始偏移量比如crashkernel512M0x10000000。但手动指定偏移很危险偏移选得不好会撞到固件预留区域或Xen/KVM内存映射所以常规环境用crashkernel512M让内核自动选位置遇到加载失败再考虑偏移。3. 从零开始实操把第一份 vmcore 抓到手3.1 动手前先确认三件事第一次配kdump我建议老老实实按以下顺序捋一遍。第一当前内核版本和发行版是否支持kdump大多数主流发行版默认开启CONFIG_KEXEC和CONFIG_CRASH_DUMP自编译内核要检查这两个配置项第二机器是否有足够的内存预留别在只有1G内存的老机器上硬上kdump第三磁盘空间够不够vmcore是内存快照大小可能和内存大小相当即使压缩后也经常有几个GB/var/crash所在的文件系统要有足够余量。我见过因为/var/crash所在的根分区写满导致vmcore写到一半失败的案例。事前检查这些能省很多事后排查的时间。3.2 完整配置流程逐步跑以Ubuntu 22.04为例完整流程大概这样。先装包apt update apt install kdump-tools crash makedumpfile然后编辑/etc/default/grub把crashkernel参数加进GRUB_CMDLINE_LINUXGRUB_CMDLINE_LINUXcrashkernel512M更新grubupdate-grub重启之后确认参数生效cat /proc/cmdline cat /sys/kernel/kexec_crash_loaded第一项要能看到crashkernel512M第二项应该是1。注意这个步骤千万不能省我通常在自动化脚本里加一步检查如果kexec_crash_loaded不是1直接报错退出免得后续白忙活。之后启用并启动kdump服务systemctl enable kdump-tools systemctl restart kdump-tools在RHEL系上配置路径是/etc/kdump.conf常用配置如下path /var/crash core_collector makedumpfile -l -c -d 31-d 31表示丢弃不需要的页31是0到4的掩码组合具体含义后面会展开。改完配置后执行kexec -p重新加载捕获内核或者systemctl restart kdump再用kdumpctl status查看状态。3.3 故意触发一次 panic验证整个链路验证环节最好的办法就是主动把系统搞崩。在确认这台机器可以随时重启的前提下执行echo 1 /proc/sys/kernel/sysrq echo c /proc/sysrq-triggerecho c会让内核强制执行panic。如果你看到机器黑屏或突然断开SSH不用慌几秒之后系统会通过捕获内核重新引导重启完成后检查/var/crash目录应该出现一个类似127.0.0.1-2025-01-01-12:00:00的时间戳目录里面有vmcore文件和vmcore-dmesg.txt。我强烈建议把这一步纳入到测试流程而不是生产环境直接做。生产环境里触发panic属于事故级别事故自动化同事会骂人的。3.4 重启后的检查与 vmcore 瘦身vmcore生成后会有一个压缩过程。makedumpfile是核心工具它有两个层面帮你减负过滤掉不需要的内存页以及压缩。-d参数用位掩码方式控制过滤级别。比如31代表丢弃预算为零的页、缓存页、自由页、未使用的页保留的是内核代码数据段、进程栈、关键数据结构。常用的组合是makedumpfile -c -d 31既开压缩又做过滤。**如果服务器内存很大建议用-c -d 31实测下来vmcore体积能减少到原来的十分之一甚至二十分之一。**不过过滤级别开得太高某些用户态进程信息会被丢掉分析问题时要意识到这一点。检查vmcore文件大小后du -sh /var/crash/*/vmcore或ls -lh /var/crash/*/能快速确认生成结果。4. kdump 不工作的那些坑我基本都踩遍了4.1 什么征兆说明 kdump 根本没生效配置完发现一切看起来正常但系统真的panic后什么都没有。我遇到最多的三种情况一是grub参数没生效进系统看/proc/cmdline根本没有crashkernel得回头检查grub配置和update-grub是否执行二是kexec_crash_loaded是0说明捕获内核没加载成功用systemctl status kdump或kexec -p的日志定位三是服务启动失败但系统模板日志没报警因为kdump服务的失败往往只是在重启后短暂出现被淹没在一堆系统日志里。还有磁盘路径的问题/var/crash如果挂载在一个不可写的远程目录或只读分区上vmcore同样写不出去。4.2 捕获内核起不来多半卡在内存和initramfs捕获内核启动失败时屏幕上通常会一闪而过内核panic信息来不及截图。想抓现场可以用串口或netconsole把捕获内核输出重定向到日志服务器但那个配置更复杂。另一个高发问题捕获内核本身内存不够用报Out of Memory导致无法完成转储。这种问题只能靠调大crashkernel解决从512M调到768M或1G。另外initramfs里缺少磁盘控制器驱动的情况也很常见特别是服务器板载RAID控制器或NVMe设备需要额外驱动模块时普通initramfs没包含这些模块捕获内核挂不上根设备。解决方法是重新生成捕获内核的initramfsDebian系执行update-initramfs -uRHEL系执行dracut --kdump rebuild。这个“内核起来但看不到磁盘”的问题我帮同事排查过好几次症状都表现为系统重启后crash目录是空的但/proc/cmdline又没问题。4.3 vmcore 写不出去的处理思路捕获内核起来了但vmcore没写成功这种问题最难排查。先看根文件系统挂载情况捕获内核使用的根设备是initramfs里配置的和主内核的根设备不完全一样。如果你用了NFS root、iSCSI这类网络存储捕获内核可能根本没有网络模块或没有等待网络就绪导致挂载失败。再看/var/crash目录权限SELinux或AppArmor也可能拦截捕获内核的写入动作。RHEL系可以暂时用ausearch -m avc查SELinux的拒绝记录Ubuntu系看AppArmor日志。我记一次非常典型的案例某台物理机用硬件RAID卡RAID卡驱动是厂商闭源模块主内核加载了但捕获内核的initramfs没打包这个驱动导致捕获内核启动后找不到磁盘设备panic现场全丢。最后用dracut --kdump rebuild --add-drivers megaraid_sas把驱动塞进去才解决。4.4 常见问题速查表为了让排查更快我把高频问题整理成一张表照着查就行现象可能原因检查方法处理方式/proc/cmdline没有crashkernelgrub没更新或参数写错位置cat /proc/cmdline修正grub配置后update-grubkexec_crash_loaded为0捕获内核加载失败dmesg\l kexec -p加偏移地址或调大crashkernel系统panic后无vmcoreinitramfs缺驱动journalctl -u kdump重新生成initramfs并包含对应驱动vmcore写一半失败磁盘满或SELinux拦截df -h、ausearch清理磁盘、放行策略捕获内核OOMcrashkernel太小看捕获内核串口输出调大crashkernelvmcore生成但crash打不开缺debuginfofile vmcore安装对应版本debug包4.5 一个配置细节建议直接养成习惯很多发行版的默认core collector是makedumpfile -l -c只开压缩没开过滤vmcore会很大。线上8G内存的机器如果不加-d 31vmcore能给你生成7G以上的文件加上-d 31之后可能只有几百M。我一般在配置里固定写成core_collector makedumpfile -l -c -d 31三个参数一个都不能删。这里-l表示压缩级别-c表示压缩页-d 31指定过滤级别。宁可少分析一些用户态缓冲区数据也要保证转储文件能顺利落盘。还有一个很容易忽略的习惯别把vmcore直接放在根分区。生产服务器根分区通常不会太大vmcore动辄几个GB建议单独挂一个大分区给/var/crash并考虑定期清理旧vmcore。我在维护的几套监控系统里专门加了一个清理脚本保留最近的3次vmcore其他自动删除。5. 拿到 vmcore 之后怎么从崩溃现场里挖线索5.1 crash 工具是第一步vmcore生成出来后用crash打开。命令格式是crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/127.0.0.1-2025-01-01-12:00:00/vmcore路径里的vmlinux就是调试符号文件。进入crash交互环境后最常用的命令依次是bt看调用栈log看内核日志缓冲区ps看崩溃时进程列表files看进程打开的文件dis反汇编指定地址dev和mount确认设备与挂载点。这套命令在gdb里都有对应物但crash针对内核数据结构做了高度定制输出比gdb友好太多。对我个人来说log命令是排查崩溃的第一选择。它能把panic之前的内核打印信息完整拉出来包括各个驱动打的各种消息很多时候问题答案就在这些日志里连调用栈都不用看。5.2 一份典型的 panic 走查流程我用一个常见的“内核空指针”案例走一遍。崩溃日志里通常有类似这样的线索BUG: unable to handle page fault for address: 0000000000000000 RIP: 0010:my_driver_ioctl0x40/0x100 [my_driver] Call Trace: my_driver_ioctl do_vfs_ioctl __x64_sys_ioctl do_syscall_64 entry_SYSCALL_64_after_hwframe看到RIP后面的函数名和偏移量基本就能锁定是哪个驱动的哪段代码然后配合dis -r my_driver_ioctl查看反汇编定位到具体指令。再用bt看完整调用链利用ps确认是哪个进程触发ioctl调用如果这个进程有异常重点排查应用侧。如果问题不是简单空指针而是死锁或RCU stall那么log的输出里会有相应的内核线程栈信息bt -a可以同时打印所有CPU的栈。总之crash这套工具链不是让你一行一行在内存里翻而是帮你精准切入问题发生时的“时间线”。5.3 后续可以考虑的扩展如果你要长期做内核问题排查建议顺手把下面这几样也搭起来。一是pstore/ramoops它能把崩溃前的一小段日志留在内存里反复读取即使kdump完全失效也能保证最后几行log不丢二是netconsole能把内核日志通过网络实时发到日志服务器三是systemd-coredump处理用户态崩溃和kdump互补用户态进程挂掉的时候能提供用户态core文件。还有一个建议是从内核非法访问检测和KASAN这类调试特性入门它们能在问题刚发生时立刻打印现场比事后分析vmcore省力很多。我个人的体会是kdump整套体系最让人舒服的地方在于“确定性”你提前准备好了一套可靠的故障现场保全机制真出事时不用靠猜。如果你的服务器会跑很多自定义内核模块或者长期处于高负载状态我建议把kdump纳入默认配置模板并定期在测试机上手动触发一次panic验证确认这套机制还活着。哪怕几个月用不上一次也比半夜被叫起来面对一台干干净净重启完成的机器强得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进