庖丁解CORE · 第一篇 初见全牛:vmcore 是什么,从哪里来
庖丁始解牛时,所见无非全牛。三年之后,未尝见全牛也。——《庄子·养生主》
初学者打开一个 vmcore,往往被它的体量震慑——动辄数 GB,文件类型标注 ELF,file命令返回的是core file,却和应用程序的 core dump 截然不同。你不知道它从何而来,也不知道里面藏着什么。这就是庖丁最初所见:一头完整的、令人茫然的全牛。
本文将讲清楚三件事:vmcore 是什么、它如何被生成、影响它质量的关键参数与常见故障。文章也顾及了架构差异(x86_64 与 ARM64),会在相关环节单独说明。
一、vmcore 内容及格式
1.1 vmcore 是什么
vmcore 是 Linux 内核在崩溃瞬间的内存快照,以 ELF 格式存储。更准确地说,它是一个经过筛选的内存转储,包含:
- 内核崩溃时所有 CPU 的寄存器状态(通过 ELF PT_NOTE 段存储)
- 内核使用的物理内存页(按过滤级别选择性保留)
- vmcore-dmesg:崩溃前的内核日志缓冲区
它不是:
- 完整的物理内存镜像(用户态进程内存默认被过滤掉)
- 应用程序的 core dump(那是单个进程的地址空间快照)
- 实时系统状态(它是崩溃那一刻的冻结帧,之后的一切都不存在)
1.2 vmcore 的常见存储形式
根据其获取与生成方式、数据过滤程度以及应用场景的不同,业界主要将其划分为以下六大核心类型:
完整内存转储 (Full Dump):最原始、最完整的转储方式。它不经过任何过滤,直接将系统崩溃瞬间的全部物理内存数据全量写入存储。其优点在于保留了百分之百的内存现场(包括所有的用户态、内核态数据及缓存),信息最为完整;缺点是体积庞大(与物理内存大小等同),在数百 GB 或 TB 级大内存服务器上,采集与写盘时间极长。
kdump 压缩转储 (Compressed Dump):目前生产环境最常用的标准格式(通常由 makedumpfile 配合 kdump 生成)。它在捕获阶段对内存数据进行压缩(支持 zlib、lzo、lz4、zstd 等算法),并在保留核心内核元数据的同时过滤掉无关数据,极大地节省了磁盘空间,提升了转储效率。
makedumpfile 过滤转储 (Filtered Dump):通过特定的过滤规则(如 high、low 或自定义级别),有选择地剥离掉对内核调试无用的数据页(例如零页、用户态进程数据、文件缓存等)。这使得生成的 vmcore 体积显著减小,兼顾了快速落盘与核心内核现场的保留。
远程传输转储 (Remote Dump):适用于本地磁盘空间不足或无盘服务器。捕获内核通过网络协议(如 NFS、SSH、Netcat、TFTP 等)将转储数据实时流式传输到远程存储服务器。其不依赖本地存储,但转储成功率受制于网络架构的稳定性。
分区转储 (Partial Dump):在极端或受限场景下,仅捕获指定的一部分物理内存范围,或只读取特定的物理内存区域。其体积极小、捕获极快,但由于丢弃了大量内存数据,无法进行全栈的深度死锁和链路还原。
dmesg / vmcore-dmesg (日志提取):这是一种极其轻量级的"降级转储"。它仅保存并提取内核崩溃前的 dmesg 环形缓冲区日志(Log Buffer)。它几乎不占用空间,在无法生成完整 core 的极端硬件故障场景下,作为纯文本提供最后的线索,但无法进行寄存器、内存块等高级调试。
此外,在虚拟化平台(如 KVM/QEMU、VMware ESXi)上,根据崩溃主体的不同(Host 崩溃、Guest 内部 panic、或者通过宿主机 virsh dump/QEMU Monitor 强行导出的物理镜像),vmcore 还会衍生出虚拟机内核 ELF 镜像和宿主机层面的 Raw 原始镜像等形态。
1.3 ELF 格式的 vmcore 内部结构综述
ELF 格式的 vmcore 是 Linux 标准转储体系(kdump)的核心产物。它严格遵循标准的 64 位 Executable and Linkable Format (ELF64) 规范,将崩溃瞬间的物理内存空间映射、CPU 寄存器状态以及内核元数据,组织成一个标准化、可被 crash、gdb 等工具直接解析的核心转储文件(ET_CORE 类型)。
标准 ELF64 规范的通用度,堪称现代计算底座的"硬通货"。它超越了物理硬件的藩篱,在 x86_64、ARM64、RISC-V 乃至 LoongArch 等截然不同的芯片架构之间,构建了一套高度统一的二进制格式标准。从磁盘上的可执行程序、运行时的动态链接库,到内核动态加载的 .ko 驱动模块,直至系统崩溃瞬间凝固的 vmcore 转储现场,皆由同一套Elf64_Ehdr和Elf64_Phdr结构体作为其元数据骨架(实际存储时可能包含 makedumpfile 私有头)。这种将"通用的数据外壳"与"异构的体系现场"完美分离的解耦设计,使得 ELF64 成为纵穿操作系统从编译到崩溃、横跨所有主流处理器架构的内核转储通用语言。
其内部拓扑结构由外至内,主要由以下四个关键部件无缝交织构成:
+------------------------------------------------------------------------+ | 1. ELF Header (ELF头部) | | - 标识魔数(e_ident)、机器架构(e_machine: x86_64/AARCH64)、e_phoff 等 | +------------------------------------------------------------------------+ | 2. Program Header Table (PHT / 程序头表) | | - 整个文件的索引总纲,描述并指向所有 PT_NOTE 与 PT_LOAD 段的物理偏移| +------------------------------------------------------------------------+ | 3. PT_NOTE Segment (元数据元段) | | +----------------------------------------------------------------+ | | | NT_PRSTATUS : 捕获崩溃瞬间每个 CPU 核心的通用寄存器快照 | | | | (x86_64: RIP/RSP/CR3; ARM64: PC/SP/TTBR0/1) | | | +----------------------------------------------------------------+ | | | NT_VMCOREINFO : 存放内核符号(如 init_task)、KASLR 偏移、页表 | | | | 级数、内核版本等关键调试索引的“黑盒子” | | | +----------------------------------------------------------------+ | | | NT_AUXV / NT_FILE / NT_TASKSTRUCT 等其他辅助元数据 | | | +----------------------------------------------------------------+ | +------------------------------------------------------------------------+ | 4. PT_LOAD Segments (物理内存段) | | - 包含1到N个连续或断续的物理地址映射块,直接映射崩溃瞬间的真实物理内存 | +------------------------------------------------------------------------+ELF Header(ELF 头部):位于文件起始位置,固定大小。它定义了文件的基本元数据,包括 ELF 魔数(Magic)、文件类别(64 位)、大端/小端序、目标机器架构(例如 EM_X86_64 或 EM_AARCH64),以及程序头表(PHT)在文件中的偏移量(e_phoff)和数量(e_phnum)。
Program Header Table(PHT / 程序头表):整个文件的"活地图"。它由一系列程序头项(Program Header Entries)组成,不包含具体的物理内存数据,而是精确描述了后续所有段(Segment)的类型、文件偏移(p_offset)、物理/虚拟地址基址(p_paddr/p_vaddr)以及段大小(p_memsz)。
PT_NOTE Segment(元数据段):存放系统关键状态和调试上下文的黄金核心区。它内部通过 Name-Type-Value 的内嵌结构包裹了多个至关重要的 Note 节点:
- NT_PRSTATUS(进程/核心状态):最为关键。它保存了崩溃瞬间,全机每一个 CPU 核心的实时运行快照,保存寄存器集及少量 task 上下文信息,以及最重要的处理器寄存器状态(Register Sets)。在 x86_64 上表现为 RIP、RSP、CR3(页表基址)等;在 ARM64 上则表现为 PC、SP、PSTATE 以及 TTBR0/TTBR1。这是 crash 工具重建崩溃现场函数调用栈(Backtrace)的唯一数学依据。
- NT_VMCOREINFO(内核关键元数据):存放内核的核心符号表物理地址(保存内核关键符号、结构偏移、页模型参数等)、内存模型的配置参数(如九级或三级页表)、Linux 内核版本号,以及对抗安全攻击的 KASLR(内核地址空间布局随机化)偏移量(kernel_offset)。调试工具必须读取此节点,才能正确消除地址随机化带来的偏移,把二进制内存反向推演回 C 语言源码。
- 其他 Note(如 NT_AUXV、NT_FILE、NT_TASKSTRUCT):保存辅助向量、内存映射文件信息等扩展上下文。
PT_LOAD Segments(物理内存段):占据了 vmcore 99% 以上体积的实体内容。每一个 PT_LOAD 段对应一段连续的物理内存银行(Memory Bank)。程序头表中的每一项分别映射到这些段,调试工具通过读取这些段的内容,在应用层虚拟重构出系统崩溃那一刻的全局物理内存全景。
![]()
二、生成机制:kexec + kdump
vmcore 的生成依赖两个机制的协同:kexec(内核执行链)和 kdump(崩溃转储框架)。
2.1 两个内核的世界
Linux 系统正常运行时,内存中实际上准备着两套内核:捕获内核(capture kernel / crash kernel)在系统启动时由 kdump 服务通过kexec -p预加载进保留区域,处于待命状态。
2.2 崩溃触发到 vmcore 的完整路径
① 生产内核触发 panic │ ▼ ② 调用 crash_kexec() ├── 保存当前 CPU 寄存器到 crash_notes 区域 ├── 向其他 CPU 发送 IPI/NMI,令其保存寄存器后停机 └── 通过 kexec 机制热切换到捕获内核 │ ▼ ③ 捕获内核启动(极简环境,无需完整初始化) ├── 通过 /proc/vmcore 接口访问生产内核的物理内存 └── 捕获内核的内存在保留区域内,与生产内核的内存不重叠 │ ▼ ④ makedumpfile 读取 /proc/vmcore ├── 按过滤级别丢弃无关页(零页、用户态页等) ├── 压缩剩余内存页 └── 写入目标路径(本地磁盘 / NFS / SSH 远端) │ ▼ ⑤ 捕获内核重启,系统恢复正常启动2.3 /proc/vmcore 是桥梁
/proc/vmcore是捕获内核暴露出的一个特殊虚拟文件,它将生产内核的物理内存映射为可读的 ELF 文件视图。makedumpfile 读它,就像读一个普通文件——但背后是对整个物理内存的访问。
三、vmcore 的产生方式
内核 panic 并非只有一条触发路径。从生产实践来看,vmcore 的来源可以分为四类:内核自发崩溃、看门狗超时、手动触发、虚拟化平台导出。理解这四类来源,在分析时就能判断崩溃是"系统自己倒下的"还是"被外力推倒的"——二者的根因分析思路截然不同。
3.1 内核自发崩溃
这是最常见的 vmcore 来源。内核检测到自身状态异常时,主动调用panic()终止运行。主要包含如下几种:
Oops 升级为 panic
内核遇到可恢复错误时产生 Oops(如非法内存访问、除零),默认情况下 Oops 只打印错误信息,不触发 panic。但开启以下参数后,Oops 会直接升级为 panic:
# 查看当前值(0=只打印,1=触发panic)sysctlkernel.panic_on_oops# 永久开启(写入 /etc/sysctl.conf)kernel.panic_on_oops=1生产环境强烈建议开启此参数。因为 Oops 之后内核状态已不可信,继续运行可能导致数据损坏,且不会留下 vmcore 供分析。
BUG() / BUG_ON() / WARN_ON()
内核代码中的断言宏,触发时的行为(需panic_on_oops=1):
| 宏 | 默认行为 | 受参数控制 |
|---|---|---|
| BUG() | 触发 Oops,直接 panic | 否,属于必然 panic 行为 |
| BUG_ON(cond) | 条件成立时触发 panic | 否,属于确定性断言机制 |
| WARN() / WARN_ON() | 打印警告,系统继续运行 | kernel.panic_on_warn=1 升级 |
# WARN 升级为 panic(有助于捕获早期异常)kernel.panic_on_warn=1空指针解引用(NULL pointer dereference)
访问虚拟地址 0(或极低地址)触发 page fault,内核无法处理后产生 Oops,进而(开启panic_on_oops时)触发 panic。这是生产环境中最高频的 vmcore 来源之一。
内核栈溢出
内核默认为每个线程分配 8KB(x86_64)或 16KB(ARM64)的内核栈。栈末尾有 canary 保护字;溢出时 canary 被破坏,内核检测后触发 panic。若开启了CONFIG_VMAP_STACK(虚拟地址映射栈),溢出会触发硬件 page fault,更早被捕获。
RCU stall
RCU(Read-Copy-Update)是内核的关键同步机制。若某个 CPU 在读侧临界区内停留时间过长(默认 21 秒),内核打印 RCU stall 警告:
# 开启后 RCU stall 直接触发 panic(便于捕获死循环或中断关闭过久的场景)kernel.panic_on_rcu_stall=13.2 看门狗超时触发
内核内置多层看门狗,监控不同粒度的"卡死"状态,触发后产生 panic 并生成 vmcore。
软锁(Soft Lockup)
监控对象:调度器是否在预期时间内运行。默认超时:20 秒内若某 CPU 未调度过其他任务,判定为 soft lockup。
kernel.softlockup_panic=1# 检测到后触发 panic(默认关闭)kernel.watchdog_thresh=10# 超时阈值(秒),soft lockup = 2×此值硬锁(Hard Lockup)
监控对象:CPU 是否在关中断状态下长时间运行(NMI 看门狗)。默认超时:watchdog_thresh秒(默认 10 秒)内若 CPU 未响应 NMI,判定为 hard lockup。实现机制:x86_64 使用 PMU(性能计数器)产生 NMI;ARM64 使用 arch timer 产生 FIQ(EL3)。
kernel.hardlockup_panic=1# 检测到后触发 panic(生产环境建议开启)kernel.nmi_watchdog=1# 启用 NMI 看门狗hung task(D 状态进程)
监控对象:长期处于不可中断睡眠(D 状态)的进程。默认超时:120 秒。常见于 NFS 挂起、存储设备无响应等场景。
kernel.hung_task_panic=1# D 状态超时后触发 panickernel.hung_task_timeout_secs=120OOM killer
内存耗尽时,OOM killer 选择进程并将其杀死。若开启以下参数,OOM 事件直接升级为 panic:
vm.panic_on_oom=1# OOM 触发 panic(适合内存泄漏定位)# 0 = 只杀进程(默认)# 1 = 内存耗尽时触发 panic# 2 = 强制 panic(忽略 cpuset/mempolicy 限制)各类看门狗的超时对比
时间轴(秒) 0────5────10────20────30────..────120 │ │ │ │ │ └── hung_task(D 状态进程,默认 120s) │ └── soft lockup(默认 20s = 2 × watchdog_thresh) └── hard lockup(默认 10s = watchdog_thresh)3.3 手动触发
某些场景下系统处于"活锁"状态——没有触发 panic,但已经失去响应。此时需要人工强制触发 vmcore。
SysRq 触发(最常用)
# 方式一:直接写入(需要 root 权限)echoc>/proc/sysrq-trigger# 方式二:键盘(物理或串口控制台)Alt + SysRq + C# 需要满足以下前提条件:# 确认 SysRq 已启用(0=禁用,1=全部启用,或按位组合)sysctlkernel.sysrq# 至少需要开启 crash dump 位(bit 7 = 128)kernel.sysrq=1echo c向内核写入字符 c(crash),内核调用panic("sysrq triggered crash\n"),之后走标准 kdump 流程生成 vmcore。这是最干净、最常用的手动触发方式。
NMI 按钮(物理服务器)
部分服务器主板提供物理 NMI 按钮。按下后 CPU 收到 NMI,触发nmi_panic()。适用于系统完全失去网络响应、SysRq 也无法操作的场景。需要提前开启:
kernel.unknown_nmi_panic=1# 收到未知 NMI 时触发 panicIPMI/BMC 注入 NMI(远程触发)
无需物理接触服务器,通过带外管理接口远程触发:
# 通过 ipmitool 发送 NMI(需要 BMC 支持)ipmitool chassis power diag# 或通过 Redfish API(现代服务器)curl-XPOST https://<BMC_IP>/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset\-d'{"ResetType": "Nmi"}'收到 NMI 后,若内核配置了unknown_nmi_panic=1,触发 panic 并生成 vmcore。这是生产环境中处理"系统失联"最常用的手段。
注意:手动触发的 vmcore,crash 报告中 panic 原因会显示为
sysrq triggered crash或NMI: IOCK error,而非真实业务错误。分析时要结合崩溃前的 dmesg 日志判断真实根因。
3.4 虚拟化平台的 vmcore 导出
在虚拟化或云环境中,获取 vmcore 的路径有所不同。
KVM/libvirt(virsh dump)
# 在宿主机上导出虚拟机内存快照virshdump<domain-name>/tmp/vm.dump --memory-only# 导出为 kdump 兼容格式virshdump<domain-name>/tmp/vm.dump--formatkdump-zlibvirsh dump导出的是虚拟机的物理内存视图,格式与 kdump 生成的 vmcore 兼容,可直接用 crash 分析。但它不依赖虚拟机内部的 kdump 配置,即使虚拟机内部 kdump 未启用也可导出。适用场景:虚拟机内核卡死、无法自动触发 kdump 时,由宿主机介入强制导出。
VMware(内存快照)
VMware 的虚拟机内存快照(.vmem 文件)格式与 ELF vmcore 不同,需要转换工具(如vmss2core)转为 crash 可读格式后才能分析。
云平台(ECS/EC2 等)
公有云厂商通常提供控制台的"发送 NMI"功能(等同于 IPMI NMI 注入),触发后走虚拟机内部的 kdump 流程,vmcore 写入虚拟机本地磁盘。部分云厂商还支持将 vmcore 直接上传到对象存储(OSS/S3)。
3.5 产生方式汇总与对比
| 产生方式 | 触发者 | 需要预配置 | vmcore 完整性 | 典型场景 |
|---|---|---|---|---|
| 内核 panic(自发) | 内核自身 | 需 kdump 就绪 | 高 | 空指针、BUG()、栈溢出 |
| soft/hard lockup | 看门狗 | 需开启 panic 参数 | 高 | 死循环、关中断过久 |
| hung task | 看门狗 | 需开启 panic 参数 | 高 | NFS 挂起、存储无响应 |
| SysRq | 运维人员 | 需 sysrq 启用 | 高 | 系统无响应但 SSH 可达 |
| IPMI/BMC NMI | 运维(带外) | 需 unknown_nmi_panic | 高 | 系统完全失联 |
| virsh dump | 宿主管理员 | 无需客户机配置 | 中(无寄存器) | 虚拟机卡死 |
| 物理 NMI 按钮 | 运维(现场) | 需 unknown_nmi_panic | 高 | 现场处理 |
四、x86_64 架构下的工作细节
4.1 CPU 停机机制
x86_64 上,生产内核 panic 时:
- 触发 panic 的 CPU(BSP 或任意 CPU)调用
crash_kexec() - 通过 APIC IPI(处理器间中断)向所有其他 CPU 发送
REBOOT_VECTOR - 其他 CPU 收到中断后执行
crash_nmi_callback(),将自身寄存器保存到crash_notes(每 CPU 一块区域)后进入 cpu_idle 等待 - 若某颗 CPU 在规定时间内未响应 IPI,主 CPU 通过 NMI 强制触发
crash_notes的物理地址通过内核符号crash_notes导出,捕获内核启动时从elfcorehdr=参数获取这块区域的位置。
APIC 的全称是 Advanced Programmable Interrupt Controller(高级可编程中断控制器)。简单来说,它是现代多核(Multi-core)计算机中负责管理和分发"中断信号"的核心硬件机制。如果把 CPU 的每个核心比作流水线上的工人,那么 APIC 就是那个精密的"任务调度员",决定哪个紧急任务(中断)该发给哪个工人、谁的优先级更高、以及工人之间如何通过暗号(核间中断)协同工作。
4.2 内存布局与 KASLR
x86_64 默认开启 KASLR(内核地址空间随机化)。每次启动时,内核的虚拟地址基址会随机偏移。crash 工具需要通过以下方式确定偏移量:
- vmcore 的 PT_NOTE 中记录了
KERNELOFFSET - crash 工具读取该值后,配合未随机化的符号表(vmlinux)计算真实地址
- 若 vmlinux 符号地址与 vmcore 中的地址对不上,通常是 KASLR 偏移未被正确处理,而非 vmlinux 版本不匹配
KASLR 的核心逻辑在于:以开机时的动态信息熵,打破黑客对静态内存地址的绝对预测。当开启 KASLR 后,系统每次引导启动(Boot)时,内核的早期引导加载程序(Decompressor)会利用硬件随机数生成器(如 CPU 的 RDRAND 指令)产生一个随机的偏移量(Offset),称为 Entropy(熵值)。内核会利用这个随机偏移量,对核心区域的虚拟地址(有时也包括物理地址)进行整体平移和打乱。
4.3 crashkernel 参数(x86_64)
要保证 kdump 机制正常工作,需要在 GRUB 启动参数中设置crashkernel参数。crashkernel是 Linux 内核启动参数,其核心功能是:为 kdump 机制预留一块物理内存,用于在系统崩溃时运行捕获内核并生成 vmcore。
x86_64 上保留区域默认放在物理内存高端(>4G 处),避免与 DMA 区域冲突。也可显式指定物理地址:
# 方式 1:静态指定大小(不限位置)crashkernel=256M# 方式 2:静态指定大小 + 强制指定物理起始地址crashkernel=256M@2G# 方式 3:阶梯式自动匹配(根据总内存大小决定预留大小)crashkernel=512M-2G:64M,2G-8G:128M,8G-:256M# 方式 4:高级拆分配置(区分高低端内存,常见于大内存服务器)crashkernel=512M,highcrashkernel=128M,low关键字由发行版预设规则自动计算:
crashkernel=auto让系统根据总内存大小,全自动、阶梯式地在物理内存(大内存时会自动区分高/低端)中预留 kdump 空间,优点是免维护、省心,缺点是大小由内核黑盒决定、容易隐形侵占业务内存。
推荐值参考(x86_64):
| 物理内存总量 | 推荐 crashkernel 大小 |
|---|---|
| < 4 GB | 128M |
| 4 ~ 64 GB | 256M |
| 64 ~ 1 TB | 512M |
| > 1 TB | 1G 或更大 |
注意:保留区域过小会导致捕获内核无法启动(最常见的 kdump 失败原因之一)。
五、ARM64 架构下的工作细节
5.1 异常级别与 panic 路径
ARM64 使用异常级别(Exception Level)模型:
EL3 — 安全监控(Secure Monitor,固件层) EL2 — Hypervisor(虚拟化层) EL1 — 内核(Kernel) ← Linux 运行于此 EL0 — 用户态(User)内核 panic 发生在 EL1。ARM64 没有 x86_64 的 APIC 机制,CPU 停机通过以下路径实现:
- PSCI(Power State Coordination Interface):通过 CPU_OFF 调用让其他 CPU 下线,由固件层(EL3)执行
- IPI via GIC(Generic Interrupt Controller):向其他 CPU 发送处理器间中断,功能类似 x86_64 的 APIC IPI
- 若 PSCI 不可用(如裸板环境),则使用
smp_send_stop()发 IPI 后等待 CPU 进入 WFI(Wait For Interrupt)状态
5.2 设备树 vs ACPI
ARM64 平台存在两类固件描述方式,影响 kdump 的配置方式:
设备树(Device Tree)平台(嵌入式、服务器早期):
crashkernel=参数行为与 x86_64 基本一致- 捕获内核通过 DTB(设备树二进制)获取硬件信息
- 需确保捕获内核使用的 DTB 与生产内核一致
ACPI 平台(现代 ARM64 服务器,如鲲鹏、飞腾、Ampere):
- 行为更接近 x86_64,
crashkernel=auto通常可用 - 捕获内核通过 ACPI 表发现硬件,无需额外 DTB 配置
注意:区分 ACPI 和 APIC
- ACPI → 系统配置和硬件发现(“系统长什么样”)
- APIC → 中断控制和 CPU 通信(“中断怎么发、CPU 怎么聊”)
在 vmcore 生成链路上:
- ACPI:决定 crashkernel 能不能正常分配(ARM64 更敏感)
- APIC:决定 panic 时能不能让所有 CPU 停下并保存寄存器(x86_64 特有)
5.3 crashkernel 参数(ARM64)
ARM64 的内存布局限制比 x86_64 更严格:
- 保留区域必须位于内核映像可寻址的范围内
- 部分平台要求保留区域对齐到 2MB 边界
- 高端内存分离写法同样适用:
crashkernel=256M,highcrashkernel=128M,lowARM64 特有问题:部分平台(尤其是内存超过 256GB 的大型服务器)需要同时指定 high 和 low,否则捕获内核的 DMA 操作会失败:
crashkernel=512M,highcrashkernel=128M,low推荐值参考(ARM64):
| 物理内存总量 | 推荐 crashkernel 大小 |
|---|---|
| < 4 GB | 128M |
| 4 ~ 64 GB | 256M |
| 64 ~ 256 GB | 512M |
| > 256 GB | 512M,high + 128M,low |
5.4 KASLR on ARM64
ARM64 的 KASLR 实现与 x86_64 类似,但随机化范围由 VA_BITS 决定(通常 48 位或 52 位虚拟地址空间)。处理方式相同:vmcore 的 PT_NOTE 中记录偏移量,crash 工具自动处理。
六、makedumpfile 过滤参数
makedumpfile 是控制 vmcore 体积与质量的核心工具,通过-d参数指定过滤级别,参数总共有 32 个过滤级别,数值范围从 0 到 31,下表为部分示例:
| 级别 | 参数 | 过滤内容 | 典型用途 |
|---|---|---|---|
| 0 | -d 0 | 不过滤,完整转储 | 深度调试,体积最大 |
| 1 | -d 1 | 过滤零页 | 轻度压缩 |
| 2 | -d 2 | 过滤零页 + 缓存页(非私有) | 常规推荐 |
| 3 | -d 3 | + 缓存页(私有) | 进阶定制过滤 |
| 4 | -d 4 | + 用户态数据页 | 默认推荐值 |
| 5 | -d 5 | + 空闲页(buddy system 中的空闲内存) | 体积最小 |
| 6 | -d 6 | + Hugepage | 特殊巨页场景 |
| 31 | -d 31 | 最大过滤,几乎只保留内核关键数据 | 网络传输场景 |
生产环境推荐:-d 31配合压缩,体积可缩小到原始内存的 5%~15%,同时保留足够的内核分析数据。若需要分析用户态进程(如调查某个进程是否触发了内核 bug),则使用-d 4或更低级别。
压缩选项: -c # zlib 压缩(最通用) -l # lzo 压缩(速度快,CPU 开销低) -p # 并行压缩(多核加速,推荐)配置文件(/etc/kdump.conf或/etc/sysconfig/kdump)中的典型配置:
core_collector makedumpfile -l --message-level 1 -d 31七、常见故障与解决方法
7.1 kdump 服务启动失败
现象:systemctl status kdump显示 failed,或/sys/kernel/kexec_crash_loaded值为 0
# 确认保留内存是否生效cat/proc/cmdline|grepcrashkernelcat/sys/kernel/kexec_crash_size# 应为非零值# 查看 kdump 日志journalctl-ukdump-b常见原因与解法:
| 原因 | 现象 | 解法 |
|---|---|---|
| crashkernel 参数未加入启动项 | kexec_crash_size 为 0 | 修改 GRUB 配置文件重启生效 |
| 保留内存(Reserved)不足 | kdump 日志显示 OOM 报错 | 增大 crashkernel 分配值 |
| 捕获内核(Capture)镜像缺失 | 日志提示找不到 vmlinuz | 确认 kdump 核心包安装完整 |
| SELinux 策略阻止内核加载 | 触发 AVC denied 日志 | setenforce 0 测试或加 policy |
7.2 panic 后未生成 vmcore
现象:系统崩溃重启,目标路径下无 vmcore 文件
# 确认目标路径挂载正常(NFS 场景尤其要检查)cat/etc/kdump.conf|grep^pathcat/etc/kdump.conf|grep^nfs# 检查磁盘空间df-h/var/crash# 查看上次崩溃的 kdump 日志journalctl-b-1|grep-ikdump常见原因:
| 原因 | 解法 |
|---|---|
| 目标路径磁盘已满 | 清理旧 vmcore,或更换存储目标 |
| NFS 挂载在捕获内核中失败 | 检查网络设备驱动是否在捕获内核 initrd 中 |
| makedumpfile 崩溃(OOM) | 减小过滤级别,或增大 crashkernel |
| 目标路径权限不足 | 确认 root 可写 |
| 二次 panic(捕获内核本身崩溃) | 查看串口/BMC 日志,通常是驱动兼容问题 |
7.3 vmcore 生成但无法用 crash 打开
现象:crash vmlinux vmcore报错退出:crash: cannot resolve "init_task"或crash: compressed kdump: uncompress error
常见原因与解法:
| 错误信息 | 原因 | 解法 |
|---|---|---|
| cannot resolve “init_task” | vmlinux 与 vmcore 内核版本不匹配 | 用crash -s确认版本,找到对应 debuginfo |
| uncompress error | vmcore 损坏或压缩格式不支持 | 检查 makedumpfile 版本,尝试-d 0重新生成 |
| ELF header is corrupted | 写入中断(磁盘满、掉电) | 检查生成时的错误日志 |
| no debugging data available | 缺少 debuginfo/vmlinux | 安装对应内核的 debuginfo 包 |
7.4 x86_64 特有:APIC 超时导致 vmcore 不完整
现象:vmcore 中部分 CPU 的寄存器快照缺失(crash 中bt -a某些 CPU 无输出)
原因:panic 发生时,部分 CPU 未在超时时间内响应 IPI,主 CPU 放弃等待直接执行 kexec。
# 内核启动参数增加:apic=verbose# 开启APIC详细日志,定位卡死CPUhpet=disable# 某些平台HPET中断干扰IPI响应# 或在 kdump 配置中允许更长的 CPU 等待时间:# /etc/kdump.confextra_bins /usr/sbin/earlykdump7.5 ARM64 特有:PSCI 调用失败导致捕获内核卡死
现象:panic 后系统无响应,串口无输出,既不重启也不生成 vmcore
原因:捕获内核尝试通过 PSCI 让其他 CPU 下线时,固件未正确响应。多见于:
- 早期 ARMv8 平台固件 bug
- 虚拟化层(EL2)拦截了 PSCI 调用
- 设备树中 PSCI 描述错误
# 捕获内核启动参数中禁用其他 CPU(单 CPU 模式捕获,牺牲完整性换可靠性)nr_cpus=1# 或指定 PSCI 调用方式psci=force_hvc# 强制使用HVC调用(虚拟化场景)psci=force_smc# 强制使用SMC调用(裸机场景)常规的处理方法是通过/etc/kdump.conf将参数传递给捕获内核,这种方法以牺牲性能,换取绝对的启动成功率。以此绕过 ARM64 复杂的 PSCI 多核和中断固件 Bug:
kdump_commandline_append="nr_cpus=1 irqpoll"7.6 vmcore 体积过大,写入超时
现象:捕获内核启动正常,但 makedumpfile 运行时间过长,最终超时或磁盘写满
# 提高过滤级别core_collector makedumpfile-l--message-level1-d31# 启用并行压缩(多核机器显著提速)core_collector makedumpfile-l--message-level1-d31-p# 或使用 SSH 实时传输到远端,避免本地磁盘瓶颈sshroot@remote-host"mkdir -p /var/crash"对于超大内存机器(>1TB),建议结合-d 31和并行压缩,并将crashkernel设置到 1G 以上,确保捕获内核本身有足够内存运行 makedumpfile。
八、小结:初见全牛的地图
看完本篇,你对 vmcore 应当已经建立这样的认知地图:
【系统启动时】 GRUB crashkernel= 参数 │ └→ 内核保留一块物理内存 └→ kdump 服务将捕获内核加载入保留区(kexec -p) 【panic 发生时】 生产内核触发 panic │ └→ 保存各 CPU 寄存器(x86_64: IPI+NMI / ARM64: GIC IPI+PSCI) └→ kexec 热切换到捕获内核 【捕获内核运行时】 /proc/vmcore 暴露生产内核内存 │ └→ makedumpfile 读取、过滤、压缩 └→ 写入目标路径 → vmcore 文件 【之后】 crash 工具 + vmlinux → 还原现场vmcore 就是这条链路的终点产物。它的完整性取决于:保留内存够不够、过滤级别对不对、目标存储来不来得及写完。
下一篇「执刀问器」,我们将配置好分析环境,拿起工具,开始正式面对这牛头。