内存管理工程实践:memblock内存分配与故障定位实战 - 第2节

2.1 三层架构:从全局到区域

memblock 分配器采用三层数据结构来管理物理内存:全局控制器 → 类型描述符 → 区域节点。这种分层设计使得内存区域的增删、遍历和属性查询都极为高效。

核心认知: 三层结构对应三种粒度——struct memblock 管理全局状态,struct memblock_type 管理同类区域集合,struct memblock_region 描述单块连续物理内存。
memblock 三层数据结构关系图 struct memblock memblock_type (memory) memblock_type (reserved) region[0] region[1] region[2] region[...] 每个 memblock_type 维护一个动态 region 数组,按地址升序排列,相邻区域自动合并

2.2 struct memblock:全局内存管家

struct memblock 是 memblock 分配器的顶层结构体,全局唯一实例为 memblock。它持有两个类型描述符(memory 和 reserved)以及当前分配状态。

// include/linux/memblock.h
struct memblock {
    bool bottom_up;                  /* 是否从低地址向上分配 */
    phys_addr_t current_limit;       /* 当前分配上限地址 */
    struct memblock_type memory;     /* 可用物理内存区域 */
    struct memblock_type reserved;   /* 已预留内存区域 */
};
字段类型说明
bottom_upbool分配方向标志:true 表示从低地址向高地址分配,false 则相反。早期启动阶段通常为 true。
current_limitphys_addr_t动态分配上限,用于限制分配范围,防止侵占关键区域。
memorystruct memblock_type记录系统所有可用物理内存的 region 集合。
reservedstruct memblock_type记录已被预留(分配出去)的物理内存 region 集合。
调试技巧: 在早期启动代码中打印 memblock.current_limit 可以快速判断当前分配器的工作范围,常用于排查 DMA 或设备内存映射冲突。

2.3 struct memblock_type:同类区域的集合

struct memblock_type 描述一组相同属性的内存区域。它维护一个动态增长的 region 数组,并保证数组按基地址升序排列、无重叠、相邻区域已合并。

struct memblock_type {
    unsigned long cnt;          /* region 数组中有效条目数 */
    unsigned long max;          /* region 数组当前容量 */
    phys_addr_t total_size;     /* 所有 region 的总大小 */
    struct memblock_region *regions;  /* 指向 region 数组的指针 */
    char *name;                 /* 类型名称,如 "memory" 或 "reserved" */
};
字段说明
cnt当前已使用的 region 数量。每次添加或删除区域时更新。
maxregion 数组的最大容量。当 cnt 达到 max 时,会触发数组扩容。
total_size所有 region 的物理大小之和,用于快速获取该类内存的总量。
regions指向 struct memblock_region 数组的指针。早期使用静态数组,后期可能重分配。
name调试用字符串,在 /sys/kernel/debug/memblock 中可见。
注意: region 数组的排序和合并操作是 memblock 的核心维护逻辑。每次插入新区域时,memblock 会执行线性扫描,合并相邻或重叠区域,确保数组始终紧凑有序。这一操作的时间复杂度为 O(n),在 region 数量较少时效率很高。
memblock_type 内部 region 数组结构 regions 数组(按 base 升序排列,无重叠) region[0] region[1] region[2] region[3] region[4] cnt = 5(有效条目) | max = 8(数组容量) | total_size = 各 region size 之和 关键不变式: regions 按 base 升序 | region[i].base + region[i].size ≤ region[i+1].base 相邻 region 之间不允许有空洞重叠,插入时自动合并

2.4 struct memblock_region:最小管理单元

struct memblock_region 描述一段连续的物理内存区域。它是 memblock 分配器操作的最小粒度,所有内存的添加、预留、释放最终都归结为对 region 的增删改。

struct memblock_region {
    phys_addr_t base;   /* 区域起始物理地址 */
    phys_addr_t size;   /* 区域大小(字节) */
    enum memblock_flags flags; /* 区域属性标志 */
};
字段说明
base物理基地址,必须与体系结构页对齐(通常 4KB)。
size区域长度,单位字节。base + size 不能溢出。
flags描述内存属性的位掩码,如可缓存、不可缓存、设备内存等。

flags 字段常用的枚举值包括:

  • MEMBLOCK_NONE (0) — 普通内存,无特殊属性。
  • MEMBLOCK_HOTPLUG — 支持热插拔的内存区域。
  • MEMBLOCK_MIRROR — 镜像内存,用于高可靠性场景。
  • MEMBLOCK_NOMAP — 不映射到内核虚拟地址空间(如某些固件保留区)。
实战要点: 在调试内存预留问题时,重点关注 flags 字段。例如,DMA 缓冲区通常要求 MEMBLOCK_NONE,而设备 MMIO 区域应标记 MEMBLOCK_NOMAP 以防止内核误映射。
struct memblock_region 内存布局示意 0x0 物理地址空间 base = 0x1000 size = 0x8000 flags = NONE 起始地址 连续长度 属性标志 region 描述一段连续物理内存:从 base 开始,长度为 size 字节,具有 flags 指定的属性

2.5 数据结构关系总结

三个结构体之间的协作关系可以概括为:

  1. 全局入口:struct memblock 是唯一全局变量,通过 memblock.memory 和 memblock.reserved 访问两个类型集合。
  2. 类型管理:struct memblock_type 维护一个动态 region 数组,提供按地址排序、合并、扩容等内部操作。
  3. 区域描述:struct memblock_region 是原子单元,记录单块连续内存的基址、大小和属性。
结构体角色关键操作
struct memblock全局控制器分配方向控制、分配上限管理
struct memblock_type区域集合添加/删除 region、排序合并、扩容
struct memblock_region最小单元记录 base/size/flags,被遍历和修改
学习路径建议: 理解这三层结构后,下一步应关注 memblock 的 添加内存(memblock_add)和 预留内存(memblock_reserve)两个核心 API,它们直接操作 region 数组的插入和合并逻辑。