Linux内核引导内存分配器原理
一、bootmem分配器
在内核初始化的过程中需要分配内存,内核提供临时的引导内存分配器,在页分配器和块分配器初始化完成之后,把空闲的物理页交给页分配器管理,丢弃引导内存分配器。bootmem 分配器定义的数据结构,内核源码如下:


每个内存节点后一个bootmem_data实例:

bootmem分配器的算法:
a. 只把低端内存添加到 bootmem 分配器,低端内存是可以直接映射到内核虚拟地址空间的物理内存;
b. 使用一个位图记录哪些物理面被分配,如果物理页被分配,把这个物理页对应的位设置为 1;
c. 采用最先适配算法,扫描位图,找到第一个足够大的空闲内存块;
d. 为了支持分配小于一页的内存块,记录上次分配的内存块的结束位置后面一个字节的偏移和后面一页的索引,下次分配时,从上次分配的位置后面开始搜索。如果上次分配的最后一个物理页剩余空间足够,可以直接在这个物理页上分配内存。
bootmem 分配器对外提供分配内存函数 alloc_bootmem,释放内存的函数是 free_bootmem。ARM64 架构内核不使用 bootmem 分配器,但是其他处理器架构还在使用 bootmem 分配器。
二、memblock分配器
1.数据结构


物理内存类型和内存类型区别:内存类型是物理内存类型的子集,在引导内核时可以使用内核参数 "mem=nn [KMG]",指定可用内存的大小,导致内核不能看见所有的内存;物理内存类型总是包含所有内存范围,内存类型只包含内核参数 "mem=" 指定的可用内存范围。
举例解释:
- 物理内存类型:代表硬件实际存在的所有内存。例如,计算机实际安装了 16GB 内存,无论内核如何配置,物理内存类型始终涵盖这 16GB 的全部范围。
- 内存类型:受内核参数控制的可用内存范围。若引导内核时使用参数
mem=10G,内核会将可用内存限制为 10GB(即只管理这 10GB),此时内存类型仅包含这 10GB,是物理内存类型(16GB)的子集。这说明通过mem参数,内核主动 “忽略” 部分物理内存,仅管理指定范围内的内存。
2.内存块类型的数据结构


3.memblock、memblock_type、memblock_region区别
1. 三个数据结构的作用关系
(1)
memblock_region
作用:描述单个物理内存区域的详细信息。
字段:
base:起始物理地址。
size:区域大小。
flags:标志位(如MEMBLOCK_NOMAP)。
nid(可选):NUMA 节点编号。示例:
一个内存区域0x1000-0x2000对应一个memblock_region,包含base=0x1000,size=0x1000,flags=MEMBLOCK_NONE。(2)
memblock_type
作用:管理同一类内存区域的集合(如“可用内存”或“保留内存”)。
字段:
cnt:当前区域数量。
max:数组最大容量。
total_size:所有区域总大小。
regions:指向memblock_region数组的指针。
name:类型名称(如memory)。示例:
memory类型管理所有可用物理内存,包含两个memblock_region(例如0x0-0x9FFF和0x100000-0x1FFFFF)。(3)
memblock
作用:全局内存分配器的顶层结构,整合所有
memblock_type并控制分配策略。
字段:
bottom_up:分配方向(从低地址向高地址,或相反)。
current_limit:物理地址上限(例如限制为 32 位地址空间)。
memory和reserved:memblock_type实例,分别管理可用和保留内存。示例:
系统初始化时,memblock的memory类型记录所有物理内存,reserved类型记录已分配的内存。
2. 协同工作流程
场景示例:系统启动时的内存管理
初始化
memblock
系统启动时,
memblock初始化两个memblock_type:
memory:初始为空,后续由 BIOS 或固件报告填充。
reserved:初始为空,记录内核保留区域。设置
bottom_up=false(默认从高地址开始分配),current_limit设置为最大物理地址。添加物理内存到
memory类型
BIOS 报告两段物理内存:
0x0-0x9FFF(常规内存)
0x100000-0x1FFFFF(扩展内存)创建两个
memblock_region,添加到memory的regions数组:// memblock_region 1: base=0x0, size=0xA000, flags=0 // memblock_region 2: base=0x100000, size=0x100000, flags=0
memory的cnt=2,total_size=0x1A0000。保留内核代码区域
内核需要保留
0x500-0x1000用于自身代码:memblock_reserve(0x500, 0xB00); // 调用 memblock API
reserved类型新增一个memblock_region:// memblock_region: base=0x500, size=0xB00, flags=0
reserved的cnt=1,total_size=0xB00。分配内存给驱动程序
请求分配
0x2000大小的内存:void *addr = memblock_alloc(0x2000, MEMBLOCK_NOMAP);memblock 从
memory的regions中查找可用区域(例如0x100000-0x1FFFFF)。分配后:
memory的对应region被分割或缩小(例如剩余0x102000-0x1FFFFF)。新分配的
0x100000-0x102000添加到reserved的regions。
reserved的cnt=2,total_size=0xB00+0x2000。
3. 关键交互过程
memblock控制全局策略:通过bottom_up决定分配方向,通过current_limit限制物理地址范围。
memblock_type分类管理内存:memory维护可用内存池,reserved跟踪已分配区域。
memblock_region提供具体信息:每个区域的基础属性由memblock_region描述,并存储在memblock_type的数组中。4.关系图如下:
4.ARM64内核初始化memblock分配器流程

在源文件“mm/memblock.c”定义全局变量memblcok,把成员bottom_up初始化为假表示从高地址向下分配。

ARM64 内核初始化 memblock 分配器过程:
a. 解析设备树二进制文件中的节点 /memory,把所有物理内存范围添加到 memblock;
b. 在函数 arm64_memblock_init 中初始化 memblock。


5.memblock分配器编程接口
memblock_add:添加新的内存块区域到 memblock.memory 中;
memblock_remove:删除内存块区域;
memblock_alloc:分配内存;
memblock_free:释放内存。

具体源码分析如下:

6.memblock内存分配器原理以及理解
主要维护两种内存:第一种内存是系统可用的物理内存,即系统实际含有的物理内存,其值从 DTS 中进行配置,通过 uboot 实际探测之后传入到内核。第二种内存是内核预留给操作系统的内存,这部分内存作为特殊功能使用,不能作为共享内存使用。
一、系统可用的物理内存
- 定义
这种内存是系统实际含有的物理内存,其值从 DTS(设备树)中进行配置,通过 uboot 实际探测之后传入到内核。- 举例
在基于 ARM 架构的嵌入式系统(如工业控制设备)中,设备有 1GB 物理内存。系统启动时,uboot 读取设备树中内存描述(如:memory@80000000 { device_type = "memory"; reg = <0x80000000 0x40000000>; }
reg表示内存起始地址(0x80000000)和大小(0x40000000,即 1GB)。uboot 探测确认后将信息传递给内核,内核后续用这 1GB 内存加载程序、分配进程等。二、内核预留给操作系统的内存
- 定义
内核预留给操作系统的内存,用于特殊功能,不能作为共享内存使用。- 举例
Linux 内核启动时预留内存(如 16MB)存放内核代码、数据结构。例如内核栈,当进程系统调用进入内核态,内核用预留内存创建内核栈,存储函数调用、局部变量等,用户进程无法将其作为共享内存使用,仅用于内核特定操作(系统调用、中断处理等)。
bootmem、memblock分配器与slab、伙伴和不连续页分配器的关系:
在 Linux 内核早期,bootmem 作为引导内存分配器,用于内核初始化阶段临时分配内存 。后来 memblock 逐渐取代 bootmem,成为新的引导内存分配器,它能更灵活地管理内存,处理内存热插拔、设备树内存描述解析等情况。
在引导阶段结束后,memblock 通常会将内存的管理工作移交给伙伴分配器(Buddy Allocator) 。伙伴分配器主要负责管理连续物理页的分配,是内核物理内存分配的基础。而 slab 分配器主要针对小对象(如内核结构体)的缓存分配,不连续页分配器(如 vmalloc)用于分配虚拟地址连续、物理地址不连续的内存,它们在后续内存管理中根据不同需求发挥作用,但一般不是直接从引导内存分配器移交管理,而是在内核后续运行过程中按需参与内存分配工作。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)