1.伙伴系统的初始化概述
硬件初始化:计算机加电后进行硬件检测。加载引导程序,将Linux内核加载到内存中。
内核初始化:内核被加载后开始初始化各个子系统。进行CPU架构相关的初始化。初始化内存控制器和其他设备驱动。
内存管理初始化:在内核初始化的过程中,会调用mm_init
函数来初始化内存管理系统。mm_init
函数会初始化包括伙伴系统在内的各种内存管理组件。
伙伴系统初始化:
- 伙伴系统的初始化通常发生在内存管理初始化阶段,即
mm_init
函数执行期间。 - 首先,初始化与伙伴系统相关的数据结构,如
struct zone
等。 - 然后,将由初始内存分配器(如memblock allocator)管理的内存空间释放到伙伴系统中。
- 在初始化过程中,会对每个节点(node)建立内存分配的zone优先级列表。
- 对于x86架构,高端内存和低端内存的伙伴系统初始化是分开进行的,一般先初始化高端内存区,再初始化低端内存区。
初始化完成:
- 完成伙伴系统的初始化后,内核就可以使用伙伴系统来管理物理内存的分配和回收。
所以伙伴系统的舒适化需要依赖memblock分配器释放到伙伴系统,因此需要先研究memblock。
2.伙伴系统初始化问题
内核如何获取内存的地址、大小。整个内存已经被分为了相同大小的页帧,如何管理他们,如何将空闲页面放到伙伴系统呢?伙伴系统的初始化通常发生在内存管理初始化阶段,即mm_init
函数执行期间。
(1)获取到内存区域的起始地址和大小(手册会规定dram区域的起始和结束)。
(2)伙伴系统的初始化(对物理内存管理)前:物理内存划分物理页帧后,需要填充到伙伴系统的链表上。系统启动初始化之后,有很多内存已经被占用了或者被保留起来了(例如dma,内核镜像等等)。因此,伙伴系统介入之前,整个内存是断断续续的。
(3)我们需要了解早期伙伴系统初始化之前的内存管理状态:此时需要先获取内存的管理信息(前面已经有memblock的管理了)。此时有一个全局变量struct memblock memblock保存了很多信息,记录了哪些被占用,占用多大等等。此时需要把空余的给伙伴系统进行管理。最终经过,memblock分配器的管理后,struct memblock memblock结构体的结构大概如下面所示,
(4)struct memblock memblock变量有两个信息:memory和reserved指针分别指向两个数组。数组里面是memblock_region类型的变量,记录了一段一段的空间。
3. 全局变量:struct memblock memblock
如上图所示,很详细。
- 可用物理内存:memblock.memory
- reserve的物理内存:memblock.reserved
-
- 内核镜像、dtb、uboot、页表
- GPU
4.编程接口
扫描物理内存上的块添加到memblock:memblock_add(phys_addr_t base, phys_addr_t size);删除使用memblock_remove(),什么情况下使用删除呢??
for_each_mem_range;遍历各个mem区域:
int memblock_reserve:主要是对memblock_type结构体reserve。该函数用于预留指定的物理内存区域,防止这部分内存被其他初始化过程使用。
int memblock_free: 主要是对memblock_type结构体free。该函数用于释放之前预留的物理内存区域,使其可用于其他用途。
memblock_add 该函数用于向
memblock
系统添加一个新的内存区域。memblock_remove 该函数用于向
memblock
系统移除一个内存区域。这些函数接口主要都是在memblock初始化过程中使用
通过调试接口查看信息:底下的reserved各个段就是memblock_type free指向的内容。
cat /sys/kernel/debug/memory
cat /sys/kernel/debug/reserved
5.memblock初始化
Linux的memblock
(内存块)系统是一种早期的内存管理机制,用于在内核完全初始化之前管理物理内存。它在内核启动早期使用,直到完整的内存管理系统(包括伙伴系统)初始化完成为止。
- 内核加载:当计算机启动时,BIOS/UEFI加载内核映像到内存,并跳转到内核的入口点开始执行。
- 基本硬件初始化:内核会进行一些基本的硬件初始化工作,比如设置CPU寄存器、中断控制器等。
- 内存检测:内核通过读取ACPI表或者通过内存检测来获取可用内存的信息。
-
- memblock模块加载:在内核加载早期,会加载
memblock
模块,用于管理内存。 - memblock初始化函数:
memblock_init
函数被调用来初始化memblock
系统。 - 初始化
struct memblock
结构体,这是memblock
的核心数据结构,用于管理内存信息。设置全局变量memblock指向struct memblock实例。注册memblock_reserve和memblock_free等函数,用于在初始化过程中预留和释放内存。在内核初始化过程中,某些内存区域需要被预留,例如用于设备映射的内存。使用memblock_reserve函数来预留这些内存区域。 - struct memblock_region定义了内存区域的起始地址、结束地址以及是否可用等属性。使用memblock_add_*函数添加新的内存区域。所以终态就是把整个内存都处理好了,放到memory或者reserved
- memblock模块加载:在内核加载早期,会加载
注意:early_init_dt_scan_memory函数主要用来初始化memory数组。early_init_fdt_scan_reserved_mem用来初始化reserved数组。最终状态就是将mem_block_memory_init_region和mem_block_reserved_init_region整个数组。
总结下来主要有两个过程:
(1)获取物理内存的起始地址和大小:直接扫描设备树文件
(2)初始化全局变量memblock的两个数组
设备树中描述了memory和reserve的节点。所有的节点信息都是通过例如