引导期内存机制与实现详解

引导期内存机制与实现详解

源码路径rk3588/kernel-6.1/mm/memblock.csparse.cinit-mm.cmm_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 的过渡

DT/架构登记内存memblock memory/reservedsparse_init 建立 page 描述符free_area_init 初始化 zonememblock_free_all 页交给 buddypage_alloc_init / kmem_cache_init

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_addreserved-memory 节点对应 memblock_reserve

2.3 释放给 buddy:memblock_free_all()

1
2
3
4
5
6
7
8
9
10
void __init memblock_free_all(void)
{
unsigned long pages;

free_unused_memmap();
reset_all_zones_managed_pages();

pages = free_low_memory_core_early();
totalram_pages_add(pages);
}

流程要点:

  1. free_unused_memmap():释放未使用 section 的 memmap 浪费
  2. reset_all_zones_managed_pages():同步 zone 的 managed_pages
  3. free_low_memory_core_early():遍历 memory region,对非保留页调用 __free_pages_memory(),页进入伙伴系统

之后 memblock_discard() 可丢弃 memblock 内部数据结构(仅保留 debugfs 需要时)。

2.4 sparse_init

sparse.csparse_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_dirmmap_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.cCONFIG_ROCKCHIP_THUNDER_BOOT_DEFER_FREE_MEMBLOCK

  • 大块内存可登记到 db[],在 defer_free_memblock 内核线程中 延迟 __free_pages_memory
  • rk_defer_init_hpages() 可推迟 HighMem 区页初始化(本配置下 ARM64 通常不涉及 HighMem)
  • 目的:加快快启路径,稍后再把 RAM 交给 buddy

排查启动内存问题时,注意 dmesgdefer_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+

文章互动

阅读 --

留言

0 条留言

正在加载留言…