Linux arm64 crashkernel 内存预留机制深度解析:kdump 内核低/高内存保留实战指南
2026/9/12 9:22:08 网站建设 项目流程

Linux arm64 crashkernel 内存预留机制深度解析:kdump 内核低/高内存保留实战指南

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

导读

本文以 Linux 内核 arm64 架构下 kdump 机制的内存预留(crashkernel memory reservation)为核心,系统讲解crashkernel=内核参数的三种配置形态(size@offsetsizesize,high/size,low)、低内存(Low memory)与高内存(High memory)的划分规则,以及内核在启动早期完成内存预留的完整实现路径。读完本文,你将掌握在 arm64 平台(含 Raspberry Pi 4 这类 DMA 寻址受限的特殊平台)上正确配置 kdump 预留内存的方法,理解默认低内存预留 128M 的由来,并能从源码层面解释"为什么我的 crashkernel 预留到了高内存区"这类问题。

kdump 机制与 crashkernel 预留的背景

Kdump 机制用于捕获已崩溃内核的 vmcore,以便后续对其进行分析。其核心流程是:在第一个(主)内核启动的早期阶段,预先保留一块连续的大块内存,用于预加载 kdump(捕获)内核以及收集 vmcore 所需的用户空间程序;一旦主内核发生崩溃(corruption),系统引导到这块预留内存中的 kdump 内核,由它把崩溃现场的内存转储(vmcore)保存下来供离线分析。这一设计源于 kernel/crash_reserve.c 中定义的crashk_rescrashk_low_res两个 resource(分别为 "Crash kernel" 与低内存 crash kernel 区域,标志为IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM)。

从源码结构看,crashkernel 预留功能由通用内核层(kernel/crash_reserve.c)与架构层(arch/arm64/mm/init.c、arch/arm64/include/asm/crash_reserve.h)协同实现,预留的内存必须能够"最小化地容纳 kdump 内核以及收集 vmcore 所需的用户空间程序",因此预留量既不能太小(导致 kdump 内核无法启动),也不宜过大(浪费正常运行系统的可用内存)。

内核参数:crashkernel 的三种配置形态

arm64 架构支持通过以下内核命令行参数在第一个内核启动早期预留内存,以便找到连续的大块内存区域;当从高内存区域预留 crashkernel 时,还需要考虑低内存预留:

  • crashkernel=size@offset:在用户指定的物理地址偏移处预留指定大小内存;
  • crashkernel=size:由内核自行搜索可用位置进行预留(推荐用法);
  • crashkernel=size,high配合crashkernel=size,low:分别指定从高内存与低内存预留的大小。

这些参数的解析入口在 kernel/crash_reserve.c 的parse_crashkernel()函数。它首先按经典语法crashkernel=X[@offset]解析;在配置了CONFIG_ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION时,若未检测到普通 crashkernel 设置,则继续尝试解析crashkernel=,high|low后缀语法,并支持可选的crashkernel=,cmaCMA 预留。解析器还内置了校验:crashkernel预留大小不能为 0,也不能大于等于系统总内存(*crash_size >= system_ram时返回-EINVAL)。

值得说明的是,parse_crashkernel()支持三种子语法:

语法解析函数说明
crashkernel=ramsize-range:size[,...][@offset]parse_crashkernel_mem()按系统内存量分档预留("扩展"语法,含冒号:
crashkernel=size[@offset]parse_crashkernel_simple()经典简单语法
crashkernel=size,[high|low|cma]parse_crashkernel_suffix()后缀语法,按高/低内存分别指定

arm64 架构侧的调用点位于 arch/arm64/mm/init.c 的arch_reserve_crashkernel():它读取boot_command_line,调用parse_crashkernel()得到crash_sizecrash_baselow_sizecma_sizehigh标志,随后依次调用reserve_crashkernel_generic()(主预留)和reserve_crashkernel_cma()(可选 CMA 预留)。该函数在bootmem_init()中被调用,且注释明确指出request_standard_resources()依赖 crashkernel 内存已预留完成,因此预留必须发生在资源注册之前。

低内存与高内存的划分

对于 kdump 预留而言,低内存(Low memory)指特定上限以下的物理内存区域,该上限通常由 kdump 内核运行所需的 DMA 能力设备可访问的地址位数决定;与 vmcore 转储无关的设备可以忽略。在 arm64 上,低内存上限并非固定值:在 RPi4 平台上是 1G,而在大多数其他系统上是 4G。在禁用了CONFIG_ZONE_(DMA|DMA32)的特殊内核中,整个系统 RAM 都视为低内存。除上述低内存之外,系统 RAM 的其余部分视为高内存(High memory)。

源码层面,这一划分体现在 arch/arm64/include/asm/crash_reserve.h:

/* Current arm64 boot protocol requires 2MB alignment */ #define CRASH_ALIGN SZ_2M #define CRASH_ADDR_LOW_MAX arm64_dma_phys_limit #define CRASH_ADDR_HIGH_MAX (PHYS_MASK + 1)

其中arm64_dma_phys_limit由 arch/arm64/mm/init.c 的dma_limits_init()在启动早期计算:在启用CONFIG_ZONE_DMA时,zone_dma_limit取设备树dma-rangesof_dma_get_max_cpu_address())与 ACPI IORT(acpi_iort_dma_get_max_cpu_address())中较小的 DMA 上限;为保证低 32 位内存中有 DMA zone,当 DRAM 起始地址低于U32_MAX时还会把zone_dma_limitU32_MAX取小;随后arm64_dma_phys_limit = max_zone_phys(zone_dma_limit)。若CONFIG_ZONE_DMA32启用且arm64_dma_phys_limit仍为 0,则回退为 32 位 DMA 上限;若仍为 0,则退化为PHYS_MASK + 1(即整个物理地址空间)。RPi4 的 1G 低内存上限正是来自其 30-bit DMA 限制(平台dma-ranges收窄了 DMA zone)。

对于未覆盖上述架构头文件的通用情况,include/linux/crash_reserve.h 提供了默认值:CRASH_ALIGN为 2M(SZ_2M)、CRASH_ADDR_LOW_MAX为 4G(SZ_4G)、CRASH_ADDR_HIGH_MAXmemblock_end_of_DRAM(),并定义了默认低内存预留大小DEFAULT_CRASH_KERNEL_LOW_SIZE为 128MB。arm64 通过自己的 asm 头文件覆盖了其中三项,使低内存上限与实际 DMA 能力动态绑定。

三种预留策略的实现细节

1) crashkernel=size@offset:定点预留

该形式要求 crashkernel 内存必须预留到用户指定的区域,若该区域已被占用则预留失败。实现上,reserve_crashkernel_generic()检测到crash_base非零时设置fixed_base = true,并将搜索区间固定为[crash_base, crash_base + crash_size),随后调用memblock_phys_alloc_range()尝试在该精确区间分配;分配失败即打印 "cannot allocate crashkernel" 告警并返回,不会在其他位置兜底。这种形式适用于对预留位置有硬性要求的场景(例如需要与固件、bootloader 约定固定地址),但要求使用者精确掌握平台内存布局。

2) crashkernel=size:按搜索顺序自动预留(推荐)

crashkernel 内存区域将按以下搜索顺序在任意可用位置预留:

  1. 内核首先在低内存区域([0, CRASH_ADDR_LOW_MAX))搜索指定大小的可用区域;
  2. 若低内存搜索失败,内核回退到高内存区域([CRASH_ADDR_LOW_MAX, CRASH_ADDR_HIGH_MAX))搜索;若高内存预留成功,则随后在低内存中做一次默认大小的预留,当前默认值为 128M,足以满足 kdump 内核的低内存需求。

注意:crashkernel=size是 crashkernel 预留的推荐选项,用户无需了解特定平台的内存布局。这一点在源码中体现得非常直观——kernel/crash_reserve.c 的reserve_crashkernel_generic()首次分配失败后,若!high && search_end == CRASH_ADDR_LOW_MAX(即本次尝试的是低内存),则将搜索区间切换到高内存并设置crash_low_size = DEFAULT_CRASH_KERNEL_LOW_SIZE后重试;高内存预留成功后,再检查crash_base >= CRASH_ADDR_LOW_MAX && crash_low_size成立与否,调用reserve_crashkernel_low()完成低内存默认预留,低内存预留失败则会释放已预留的高内存区域并整体失败(避免出现"有高无低"的不完整状态)。

3) crashkernel=size,high 与 crashkernel=size,low:高低内存分别指定

crashkernel=size,(high|low)是对crashkernel=size的重要补充,允许用户分别指定需要从高内存和低内存分配多少内存。在许多系统上低内存非常宝贵,应尽量将 crashkernel 预留控制到最小。

预留流程如下:

  • 首先尝试从高内存区域为crashkernel=size,high预留内存;若预留成功,随后进行低内存预留;
  • 若高内存预留失败,内核回退到低内存区域按crashkernel=,high中指定的大小搜索;若低内存预留成功,则无需再额外做低内存预留。

注意事项:

  • 若未指定crashkernel=,low,将自动进行默认的低内存预留(128M);
  • 若指定crashkernel=0,low,表示有意省略低内存预留。

parse_crashkernel()中,当crashkernel=,high解析成功但未检测到crashkernel=,low时(返回-ENOENT),*low_size会被赋值为DEFAULT_CRASH_KERNEL_LOW_SIZE(128MB);而crashkernel=0,low则会解析出low_size = 0,配合reserve_crashkernel_generic()crash_low_size为 0 时跳过低内存预留的逻辑,实现"有意省略"。

预留对齐与资源注册

crashkernel 预留遵循 2MB 对齐(CRASH_ALIGN = SZ_2M),这源于当前 arm64 启动协议对 2MB 对齐的要求。预留完成后,内核通过insert_resource()crashk_res(以及HAVE_ARCH_ADD_CRASH_RES_TO_IOMEM_EARLY时的crashk_low_res)注册进iomem_resource,从而在/proc/iomem中以 "Crash kernel" 资源形式暴露给用户空间(例如kexec-tools据此定位预留区)。同时,crashk_res/crashk_low_res在 kernel/crash_core.c 中被进一步用于 kdump 相关导出,是kdump用户态工具完成"预加载捕获内核"的物理基础。

实战配置示例与注意事项

在实际部署 kdump 时,可结合平台情况选择以下配置:

# 方案一(推荐):完全交由内核自动搜索,无需了解内存布局 crashkernel=512M # 方案二:大内存预留放在高内存,低内存只保留最小必要量 crashkernel=512M,high crashkernel=128M,low # 方案三:有意省略低内存预留(低内存极度紧张时慎用) crashkernel=512M,high crashkernel=0,low # 方案四:指定精确物理地址(需对平台内存布局有把握) crashkernel=256M@0x50000000 # 方案五:扩展语法,按系统内存量分档(可选配 CMA 预留) crashkernel=0M-2G:128M,2G-8G:256M,8G-:512M

配置要点与限制:

  • 预留大小校验:解析器会拒绝大小为 0 或大于等于系统总内存的配置(返回-EINVAL),配置前应确认crashkernel=size的 size 小于系统 RAM。
  • 对齐要求:预留区域按 2MB 对齐,指定size@offset时 offset 也应遵循 2MB 对齐,否则分配行为以memblock_phys_alloc_range的实际结果为准。
  • 低内存上限差异:RPi4 平台低内存上限为 1G,大多数其他 arm64 平台为 4G,而CONFIG_ZONE_DMA/DMA32均被禁用时整个 RAM 都是低内存。判断当前平台上限,可查看启动日志中 crashkernel 预留打印("crashkernel reserved: ...")与/proc/iomem中的 Crash kernel 区域位置。
  • 预留失败的观测:若预留失败,内核会打印pr_err("cannot allocate crashkernel low memory ...")pr_warn("cannot allocate crashkernel ..."),可在dmesg中定位原因(通常是内存碎片导致无法找到连续大块,或低内存不足)。

总结

arm64 的 crashkernel 预留机制通过"架构层动态确定低内存上限 + 通用层统一解析与搜索分配"的分层设计,既保证了crashkernel=size的易用性(用户无需了解平台布局),又通过crashkernel=size,high/size,low为低内存紧张的场景提供了精细控制手段。理解 Documentation/arch/arm64/kdump.rst 所描述的三种语法及其在 kernel/crash_reserve.c、arch/arm64/mm/init.c 中的实现路径,将帮助你在各类 arm64 平台(含 RPi4 等 DMA 受限平台)上快速、正确地完成 kdump 内存预留配置,并为 vmcore 的稳定捕获打下基础。

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询