
你有没有半夜被电话叫醒登录跳板机一看服务器核心服务全部不可用SSH敲进去/var/log/messages 往后一翻最后一行停在某个时间点之后一片空白我遇到过。做Linux运维这些年最怕的不是业务流量大而是内核直接崩溃。内核一旦panic用户态程序、syslog、监控全部失效如果这时候手上没有一张内存快照后面几天的排查基本都是瞎猜。而这张快照就是Linux内核崩溃转储机制——kdump。这篇文章不聊教科书定义我直接把kdump从原理到配置再到实战分析完整捋一遍让你看完就知道它是怎么救命的以及怎么自己动手搭一套。1. 内核panic之后系统还剩下什么1.1 一个真实的生产环境崩溃现场先还原一个典型的故障场景。一台跑着Java服务的物理机某天下午突然失联ILO上看到控制台出现一堆Kernel panic - not syncing刷屏。此时这台机器表面上死了但CPU还在执行内存里的数据还都在只是内核已经丧失了继续管理文件系统、网络栈、进程调度的能力。你连SSH都进不去因为网络栈早就停了。这种情况下传统的手段非常有限。第一反应是看串口日志或者ILO日志但多数时候只能看到panic之前几十行输出。真正导致panic的代码路径、当时的函数调用栈、内存里的关键结构体全都没有记录。重启之后内存一清空现场就没了。kdump要解决的就是把这个现场保留下来。它并不是崩溃时才开始工作的而是在系统还健康的时候就已经铺好了后路。它会在你的主内核旁边放下一个备用内核捕获内核并预留一块独立的内存区域。一旦主内核panic系统会立刻切换到备用内核由这个备用内核接管硬件把主内核崩溃那一刻的内存完整倒腾到磁盘上。1.2 kdump的两个内核到底什么关系很多人第一次听说kdump会懵内核不是在内存里跑着吗怎么还能再塞一个这里的关键是Linux的kexec机制。kexec允许你在当前内核运行的状态下直接加载并跳转到另一个内核不走BIOS引导重启速度几乎可以忽略。kdump就把这套机制接过来系统启动时把捕获内核用kexec加载到一块专门预留的内存区域这块区域通过内核启动参数crashkernel来指定主内核正常情况下完全不去碰它。当主内核崩溃会触发一个特殊的软中断跳转到捕获内核去执行。捕获内核启动后会以一个小而干净的系统身份运行它的唯一任务就是把主内核崩溃时的物理内存内容落盘生成一个vmcore文件。这个vmcore不光是内核栈而是整个主内核镜像的物理内存副本。分析排障时你可以把它理解成一份案发现场的3D扫描图。配合未压缩的vmlinux符号文件和crash工具能把内核崩溃前每一帧调用、每个关键数据结构都翻出来定位到具体驱动甚至哪一行代码。这个设计还有一个好处捕获内核和主内核是隔离的哪怕主内核把内存和PCIe设备搞得一团糟捕获内核也能在预留区域里以最保守的方式启动尽量不依赖故障设备。所以kdump的可靠性比传统的内核崩溃日志高得多。2. 有kdump和没kdump的排查是两条完全不同的路2.1 没有转储时的排查方式有多大局限没有kdump的时候排查内核崩溃基本只能靠这么几条路。一是翻ILO/串口日志拿到panic时内核自动打印的调用栈和部分寄存器信息。通常崩溃原因如果能直接看panic字符串猜出来那还行比如Out of memory and no killable processes。但这类问题往往只是诱饵真正埋雷的模块可能要往前翻几百行而串口日志的buffer早被刷掉了。二是重启后跑内存检测或者更换硬件试试运气。内存坏块、CPU高温、磁盘控制器固件bug这些都会导致随机panic没有现场镜像你只能靠概率去试。最痛苦的是那种不定期崩溃一周一次每次日志都指向不同位置后来发现是某款网卡驱动的DMA bug但当时没有vmcore根本没法确定。三是靠内核自带的crashkernel支持的pstore/ramoops它只能保存一小段panic前后的内核日志而且需要硬件支持重启后保留。问题是它保存的是日志而不是内存对于深层次的逻辑问题信息量远远不够。2.2 拿到vmcore之后排查路径立刻变清晰有了kdump重启后你会看到类似 /var/crash/2024-06-15-10:32/vmcore 的文件以及一个完整的dmesg记录。你只需要一条命令就能进入分析crash /usr/lib/debug/boot/vmlinux-$(uname -r) /var/crash/2024-06-15-10:32/vmcore进入crash交互环境后先看调用栈crash bt PID: 2487 TASK: ffff9f7c6d0a8000 CPU: 2 COMMAND: java #0 [ffff9f7c6e537c88] machine_kexec at ffffffff8105b6fe #1 [ffff9f7c6e537ce8] __crash_kexec at ffffffff8111e5b2 #2 [ffff9f7c6e537da8] panic at ffffffff810632c1 ... #7 [ffff9f7c6e537f08] do_page_fault at ffffffff8107f1b5看到没不只是显示panic连触发panic的进程、触发的异常类型都带出来了。再配合log命令看完整内核环缓冲信息kmem -i看内存状态files看特定进程打开的文件一条完整的因果链就拉开了。没有kdump的时候即便你能拿到panic的文本输出也很少能还原到具体是哪个进程、哪块内存、哪条数据结构链导致了崩溃而crash工具可以。2.3 谁最需要kdump只要你是Linux服务器环境不管物理机还是虚拟机都建议装。尤其是这几类场景跑数据库的机器Oracle、MySQL、PostgreSQL状态敏感一次panic可能导致数据文件损坏网络设备/网关这类7x24小时在线业务内核版本或者驱动不是官方默认的服务器比如自己编译了内核、用了厂商定制驱动还有发生在内存、存储、驱动层的隐性故障这些问题的排查几乎只能靠vmcore。虚拟机上kdump同样适用只要宿主机允许guest加载kexec并预留crashkernel内存常见的KVM、VMware都能正常工作。3. 动手之前先把环境准备好3.1 内核和CPU架构的支持情况kdump依赖的内核特性在2.6.26之后就有了现在的发行版默认都编译进去了。不过还是有必要先确认一下避免装完包发现服务起不来然后白折腾半天。# 确认内核支持kexec grep KEXEC /boot/config-$(uname -r) CONFIG_KEXECy # 确认支持kdump grep CRASH_DUMP /boot/config-$(uname -r) CONFIG_CRASH_DUMPy对于x86_64和aarch64这两个主流架构kdump支持得很完善。aarch64上要注意的是某些厂商的固件对kexec冷启动支持有问题需要开启相关ACPI表传递参数常见发行版会自动处理。RISC-V架构还在完善中不建议直接用它跑生产环境kdump。如果是自己编译内核务必打开以下选项CONFIG_KEXECy CONFIG_CRASH_DUMPy CONFIG_PROC_VMCOREy CONFIG_ELF_COREy另外需要确保捕获内核的内核模块路径可以被找到。很多发行版把捕获内核的模块放在/usr/lib/modules/kernel-version下和主内核同一套模块。因为捕获内核和主内核版本相同模块是通用的。3.2 安装kdump-tools注意发行版差异大部分系统用图形化或最小化安装时kdump相关组件不会自动装。Debian/Ubuntu系sudo apt install kdump-tools crashRHEL/CentOS/Rocky系sudo yum install kexec-tools crash这里的crash不是应用崩溃而是那个内核内存分析工具。安装完crash很关键没有它你只能拿到vmcore却无法解读。RHEL系还需要额外确认crash对应的debuginfo包是否存在因为命令行比重编译内核时用的vmlinux文件就靠它来提供对于RHEL/CentOS 8及以上需要启用debuginfo仓库然后安装kernel-debuginfosudo dnf install kernel-debuginfo-$(uname -r)Ubuntu通常只需要安装linux-image- -dbgsym这步很多初次接触的人容易漏掉导致后面分析vmcore时crash报vmlinux not found。安装完成后把kdump服务设置为开机启动sudo systemctl enable --now kdump3.3 crashkernel预留内存怎么算这是kdump配置里最没标准答案的一环也是最容易踩坑的。crashkernel 参数用来告诉内核这块内存地址范围给我留住主内核不要用留着给捕获内核跑。预留太大浪费内存预留太小捕获内核启动时不够用可能panic或者写不了vmcore。实践中可以用下面这些参考值起步物理内存大小推荐crashkernel值4GB - 8GBcrashkernel256M8GB - 16GBcrashkernel512M16GB - 64GBcrashkernel768M64GB以上crashkernel1G配置写入GRUB的kernel命令行。RHEL系在/etc/default/grub的GRUB_CMDLINE_LINUX中加入GRUB_CMDLINE_LINUX... crashkernel512M然后重新生成grub配置sudo grub2-mkconfig -o /boot/grub2/grub.cfgUbuntu/Debian则是修改/etc/default/grub后执行sudo update-grub除了固定大小也可以使用crashkernelauto内核会按物理内存的一定比例自动分配。但据我的实测经验很多发行版的auto值在2GB以内的小内存机器上可能不够用而大内存机器上又可能过度分配。所以生产环境最好是手工指定一个明确的大小。系统重启后可以用如下命令确认预留内存是否生效cat /proc/iomem | grep -i crash看到输出中有 Crash kernel 标记说明预留成功了。比如1c000000-1c13afff : Crash kernel4. 核心配置kdump.conf 与服务参数4.1 配置文件里每项到底在干嘛kdump-tools的配置文件在Debian系是/etc/default/kdump-toolsRHEL系是/etc/kdump.conf。其实两者思路一致很多参数名也相同。我现在以RHEL系的 /etc/kdump.conf 为例把它最常用的几个参数拆开讲。# 指定转储文件的保存路径 path /var/crash # 核心转储文件名前缀默认是vmcore core_collector makedumpfile -c -d 31 # 是否压缩 # 不推荐改makedumpfile 参数里已经控制 # 崩溃转储时用的文件系统类型通常无需修改path最直接就是决定vmcore写到哪儿。默认写回根分区的/var/crash但生产环境建议单独挂载一个大分区比如/kdump然后设path /kdump。理由很简单根分区若是因为磁盘写满或文件系统问题导致panic转储时你再往同一个分区写大概率失败相当于案发现场也被淹了。core_collector可以理解为压缩和过滤工具默认是makedumpfile后面带两个参数很关键-c表示压缩生成的文件会显著缩小vmcore体积。一个64GB内存的机器如果不加压缩生成文件可能直接60GB加了-c通常可以压到20GB以内具体看内存页中有效数据比例。-d 31表示过滤掉哪几类内存页。31在二进制里是11111也就是过滤掉zero页、cached页、cache-private页、user-data页、free页。这么一过滤vmcore里只剩下对内核本身比较有诊断价值的内存通常分析内核panic足够而且文件大小能再缩小一半甚至更多。如果不确定该过滤哪些页保守一点可以用-d 15过滤zero/free/cache/user-data保留更多用户态内存。4.2 服务参数与启动顺序问题RHEL系还有一个/etc/sysconfig/kdump里面可以设置KDUMP_COMMANDLINE_APPEND给捕获内核追加命令行参数。比如捕获内核因为IRQ冲突起不来你可以尝试加上KDUMP_COMMANDLINE_APPENDirqpoll maxcpus1 reset_devicesirqpoll强制中断轮询避免某些设备中断无法注册。maxcpus1让捕获内核只用单个CPU减少初始化复杂度。reset_devices让内核重新初始化设备绕过主内核遗留的设备状态。这些参数在排查捕获内核启动失败时非常有用。正常情况下不需要配置但要是出现了kdump服务起来了但一触发就黑屏卡死多半就需要它们。Debian/Ubuntu系的/etc/default/kdump-tools里需要确保配置了KDUMP_CORE_COLLECTORmakedumpfile -c -d 31和USE_KDUMP1。很多默认安装的Ubuntu Server最小化环境里USE_KDUMP是0不手动改成1即使安装了服务也不会启用kdump这个坑我遇到过不止一次。4.3 SELinux和AppArmor的影响RHEL系默认SELinux是enforcing模式如果不对kdump相关进程放行vmcore很可能写不出来。实际上发行版自带的SELinux policy通常已经给/usr/sbin/kdump和/usr/bin/makedumpfile打了允许标签一般不用动。但如果你的环境里改了SELinux类型或者自定义了转储目录比如在/data下新建了一个目录给vmcore用这个目录的SELinux上下文默认可能是default_t这时候捕获内核写入就会被拒。可以这样处理sudo semanage fcontext -a -t kdump_var_crash_t /data(/.*)? sudo restorecon -R /dataUbuntu的AppArmor也有类似问题一般默认profile已经允许不需要额外配置。但如果你自定义了路径建议先临时把/etc/apparmor.d/usr.sbin.kdump-tools里的限制改成允许或者直接暂用/var/crash验证环境没问题再调。5. 触发一次真实崩溃看看vmcore长什么样5.1 模拟崩溃的正确方式测试kdump最稳妥的方式是触发一次可控的内核panic。不要直接在生产环境做我一般先在测试机上跑一遍完整流程。这个操作需要root权限而且会真的重启机器最好提前告知团队。# 让sysrq生效可能已经默认开启 echo 1 /proc/sys/kernel/sysrq # 手动触发一次内核panic echo c /proc/sysrq-trigger执行完最后一条命令控制台一闪而过系统黑屏几秒随后应该能看到捕获内核开始启动的日志最后停在Kdump: saving vmcore之类的状态上。整个过程大约根据内存大小等待几十秒到几分钟然后机器会自动重启回主内核。注意这里一定要确认dmesg里没有输出Failed to get crash kernel之类的信息否则触发后机器可能直接重启走BIOS而不是走kdump的捕获内核那就白测了。触发之前先确认服务是active状态systemctl status kdump ● kdump.service - Crash recovery kernel arming Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2024-06-14 10:22:19 UTC; 3 min ago5.2 重启后的检查清单机器自动重启后第一件事看/var/crash或者你配置的路径下有没有新的时间戳目录ls -la /var/crash/ drwx------ 2 root root 4096 Jun 15 10:32 2024-06-15-10:32目录里一般包含vmcore和vmcore-dmesg.txt。vmcore-dmesg.txt是崩溃时内核日志的提取版本可以先看它快速判断崩溃原因head -n 50 /var/crash/2024-06-15-10:32/vmcore-dmesg.txt如果看到类似Oops[#1]或BUG: unable to handle kernel NULL pointer dereference at ...的字样说明问题类型一目了然。而完整分析还是要靠crash工具读vmcore。5.3 crash工具的常用分析套路进入crash会话后我最常用的命令就这几个。crash log打印崩溃前内核环形缓冲区的全部日志比vmcore-dmesg.txt里面的信息更完整可以看到从开机到panic的全过程。crash bt打印崩溃时所有CPU的调用栈。如果崩溃发生在某个驱动中断上下文这里能看到具体的中断处理函数。比如你怀疑网卡驱动问题bt里出现的ixgbe_poll、e1000_intr这类函数名就是线索。crash files -p 2456查看某个PID打开的文件排查崩溃前进程在操作什么文件。这一招在文件系统相关panic上特别有效能迅速缩小到哪个挂载点、哪个文件。crash kmem -i查看内存分配器的整体状态观察是否有内存碎片、slab异常。crash ps查看崩溃时的所有进程状态。有时候panic并不对应当前进程的上下文而是别的进程触发了某条内核路径用ps能帮你找目击证人。最后退出用exit。crash工具生成的日志文件还可以直接保存比如crash log /tmp/vmcore.log方便事后在主环境里慢慢翻。6. 踩坑实录我遇到的kdump不工作案例6.1 crashkernel预留失败服务怎么都起不来有次在一台配有64GB内存的机器上装完kexec-toolssystemctl status kdump 一直显示 failed。journalctl -u kdump里写着 Cannot assemble a crash kernel但/proc/iomem里明明有Crash kernel区间。后来才发现问题出在触发panic时捕获内核根本没法启动因为它找不到root设备。原因是这台机器用的根文件系统是LVMmdraid捕获内核那套小型initramfs没有完整带上mdadm和lvm2模块。解决方案是在/etc/sysconfig/kdump里启用KDUMP_PRESET_CMDLINE或者手动把initramfs里缺的工具补上sudo dracut --add-modules mdraid lvm --kver $(uname -r)另一个更常见的问题是预留内存太小比如用crashkernel128M去跑一个带一堆驱动的生产内核捕获内核在解压initramfs时就OOM了。所以如果你看到捕获内核卡在Failed to allocate memory或直接重启成黑屏优先去检查crashkernel值。6.2 触发崩溃后机器既不走kdump也不进捕获内核这种情况我遇到过一次表现为执行echo c /proc/sysrq-trigger后服务器直接断电重启BIOS引导重新进来完全绕过了kdump。查看dmesg发现下面这行kexec: crashkernel reserved region is not in usable memory这是因为主内核在分配crashkernel区域时那块内存落在了被BIOS标记为不可用的UUID区间里。解决办法是给crashkernel指定一个具体的起始地址比如crashkernel512M0x40000000这样强制内核在指定地址区间预留。地址选择要看/proc/iomem中System RAM的空闲区域不同机器不同需要先查再写。6.3 vmcore写不到位捕获内核成功但目录空白还有一回捕获内核确实启动了控制台上也能看到 saving vmcore 的字样但/var/crash下却什么都没有。后来发现是因为转储路径所在分区已经满了而且我设的path /data/crash没提前挂载捕获内核尝试挂载/data时失败只能放弃写入日志里也写了 Cannot mount /data。这个教训很直接kdump的转储目录必须是一个独立、容量充足、且能被捕获内核可靠挂载的分区。建议在配置里加上一个额外测试把目录提前挂到一个新分区比如用/kdump作为独立挂载点然后df -h确认容量。vmcore大小和物理内存差不多即使压缩后也常常到10GB以上转储分区建议至少预留物理内存大小的1.5倍。7. 进阶玩法把kdump用到更顺手7.1 kdump和kexec的关系别再绕晕很多人会把kdump和kexec混为一谈。kexec是用新内核替换当前内核的通用机制不涉及崩溃。比如你想用另一套内核快速重启机器可以直接kexec -l new_kernel kexec -e跳过BIOS。kdump只是kexec的一种特殊应用它在系统panic时自动跳转到捕获内核。理解这层关系后调试kdump时你会习惯先看kexec和捕获内核的加载命令是否成功。我自己排查问题时常用一条命令验证捕获内核是否已经加载cat /sys/kernel/crash_loaded输出为1说明捕获内核已经被kexec加载进预留内存。如果为0kdump服务还停留在武装状态。7.2 makedumpfile的内存页过滤细节makedumpfile的-d参数是过滤位掩码每个bit代表一类页。为了平衡分析价值与文件大小我通常用-d 31过滤掉zero、cache、user-data和free页。但要小心如果你要排查的是用户态进程触发的崩溃把user-data页过滤掉可能会丢失关键的进程内存。比如某个JVM事故崩溃点是JNI代码里的本地方法这时保留一些用户态内存反而有用。这种情况下可以把-d从31降到-d 11保留user-data过滤zerocachefree让vmcore体积大一些但保留的证据更充分。压缩参数-c是给处理器施压换空间在CPU资源充足的机器上推荐。如果崩溃的机器本身CPU已经很弱或者为了快速落盘避免数据丢失也可以不用-c直接加大转储分区空间来换时间。kdump的落盘速度越快系统重启越快业务影响越小。7.3 块设备、网络转储和现代硬件逻辑卷、LVM、mdraid、NVMe这些捕获内核虽然都能用但它们的驱动和工具必须包含在捕获内核的initramfs里。有的发行版默认initramfs不包含nvme-cli或者mpt3sas驱动导致崩溃时捕获内核无法识别NVMe盘。这时候要么手动修改/etc/sysconfig/kdump里的KDUMP_COMMANDLINE_APPEND要么重建initramfs补驱动模块比如sudo dracut --add-drivers nvme --kver $(uname -r) --force如果担心本地磁盘不可靠kdump还支持把vmcore直接写到远程机器。在/etc/kdump.conf里可以配置NFSnfs my-nfs-server.example.com:/export/kdump或者配置ssh转储用scp推过去。使用网络转储时要额外注意捕获内核的网络驱动也要在initramfs里而且网络配置必须静态设置不能依赖DHCP否则捕获内核起来后联网失败转储照样失败。如果你有三层交换机或存储网络优先用独立的物理网口来做kdump网络不要和业务流量共享一个口踩雷。我个人在配置完kdump后一定会做一次定时自检比如每个季度手动触发一次panic模拟在测试机或维护窗口验证捕获内核、转储路径、分析流程都没问题。kdump这种功能平时用不上真正用上的时候你却希望它绝对可靠。与其到时候在电话里被催着看一个没有vmcore的panic日志不如现在就花半小时把这条链路完整走通。