引导期内存机制与实现详解
源码路径:
rk3588/kernel-6.1/mm/memblock.c、sparse.c、init-mm.c、mm_init.c
内核版本:Linux 6.1(RK3588 / ARM64)
文档目录:linuxDoc/mm/
目录
一、原理
1.1 为什么需要 memblock
内核启动早期,伙伴分配器(buddy) 与 SLUB 尚未初始化,但架构代码、设备树解析、CMA 预留等已经需要“分配/保留”物理地址范围。此时使用 memblock 作为临时分配器:
- 将系统视为若干 连续物理区间(region)
- 区分 可用内存(memory) 与 已保留(reserved)
- 只做 线性遍历与合并,不做细粒度 free list
1.2 从 memblock 到 buddy 的过渡
1.3 稀疏内存(SPARSEMEM)
ARM64 通常启用 CONFIG_SPARSEMEM:物理内存按 section 分段,只为实际存在的 section 分配 struct page 数组,避免为空洞内存浪费静态数组。
可选 CONFIG_SPARSEMEM_VMEMMAP:用虚拟映射的 vmemmap 降低元数据占用。
1.4 init_mm
全局 init_mm 是内核初始地址空间(swapper_pg_dir),所有内核线程在借用用户 mm 之前依赖它;与用户进程的 mm_struct 分离。
二、实现方式
2.1 memblock 核心结构
memblock.c 维护:
| 类型 | 变量 | 含义 |
|---|---|---|
| memory | memblock.memory |
内核可见 RAM |
| reserved | memblock.reserved |
已保留(内核镜像、DTB、initrd、CMA 等) |
| physmem | physmem(可选) |
实际物理内存视图 |
每个 region 为 struct memblock_region { base, size, flags, nid }。
2.2 登记与保留 API
| 函数 | 作用 |
|---|---|
memblock_add() / memblock_add_node() |
添加可用内存 |
memblock_reserve() |
标记保留,不再作为可分配 RAM |
memblock_remove() |
移除某段 memory |
memblock_alloc() |
早期从 memory 中切一块(仍非 buddy) |
架构在 setup_arch() 中解析 DT 后调用 memblock_add;reserved-memory 节点对应 memblock_reserve。
2.3 释放给 buddy:memblock_free_all()
1 | void __init memblock_free_all(void) |
流程要点:
free_unused_memmap():释放未使用 section 的 memmap 浪费reset_all_zones_managed_pages():同步 zone 的 managed_pagesfree_low_memory_core_early():遍历 memory region,对非保留页调用__free_pages_memory(),页进入伙伴系统
之后 memblock_discard() 可丢弃 memblock 内部数据结构(仅保留 debugfs 需要时)。
2.4 sparse_init
sparse.c 中 sparse_init():
- 为每个 online section 调用
sparse_init_one_section() - 建立 pfn ↔
struct page映射 - 与
pfn_valid()、NODE_DATA()等后续 MM API 衔接
2.5 free_area_init 与 page_alloc_init
在 page_alloc.c:
free_area_init(max_zone_pfn):按 zone 切分 PFN 范围,初始化pg_data_t、水位线、空闲链表头page_alloc_init():注册 CPU hotplug 回调,初始化 per-CPU page lists(zone_pcp_update)
2.6 mm_init.c
mm_init.c 提供 调试验证(CONFIG_DEBUG_MEMORY_INIT):打印 zonelist、pageflags 位域布局,不参与运行时分配。
2.7 init-mm.c
静态定义 struct mm_struct init_mm,绑定 swapper_pg_dir、mmap_lock、maple tree 根等,供内核态页表操作使用。
三、关键数据结构与接口
| 符号 | 文件 | 说明 |
|---|---|---|
memblock_add |
memblock.c ~747 | UMA 添加内存 |
memblock_reserve |
memblock.c ~891 | 保留物理范围 |
memblock_free_all |
memblock.c ~2279 | 交接 buddy |
sparse_init |
sparse.c ~559 | 稀疏 memmap |
free_area_init |
page_alloc.c ~8393 | zone 初始化 |
init_mm |
init-mm.c | 内核 mm |
四、RK3588 平台说明
4.1 设备树与 reserved-memory
RK3588 在 DT 中常见:
linux,cma默认 CMA 池- 显示/相机/VPU 专用
reserved-memory
这些在 arch/arm64 或 early boot 中转为 memblock_reserve(),在 memblock_free_all 时 不会 进入 buddy 空闲链表。
4.2 Rockchip Thunder Boot 延迟释放(本树特有)
memblock.c 含 CONFIG_ROCKCHIP_THUNDER_BOOT_DEFER_FREE_MEMBLOCK:
- 大块内存可登记到
db[],在defer_free_memblock内核线程中 延迟__free_pages_memory rk_defer_init_hpages()可推迟 HighMem 区页初始化(本配置下 ARM64 通常不涉及 HighMem)- 目的:加快快启路径,稍后再把 RAM 交给 buddy
排查启动内存问题时,注意 dmesg 中 defer_free_memblock 相关 pr_info。
4.3 ARM64 无 highmem
RK3588 64 位内核一般 不使用 highmem.c 热路径;可用内存在 ZONE_NORMAL(及 ZONE_DMA32)。
五、调试与观测
| 手段 | 说明 |
|---|---|
CONFIG_DEBUG_FS + CONFIG_ARCH_KEEP_MEMBLOCK |
debugfs /sys/kernel/debug/memblock/{memory,reserved} |
| 启动 log | mem=、earlycon 下 memblock 区域打印 |
mminit_verify_zonelist |
mm_init.c,需 mminit_loglevel |
附录:源码索引
| 主题 | 文件 | 约略行号 |
|---|---|---|
| memblock 释放 | memblock.c | 2279+ |
| RK defer free | memblock.c | 2145–2204 |
| sparse 初始化 | sparse.c | 559+ |
| zone 初始化 | page_alloc.c | 8393+ |
| init_mm | init-mm.c | 30+ |
正在加载留言…