"Boot time memory management"(引导时间内存管理)
早期系统初始化不能简单地使用“正常”的内存管理,因为它还没有设置。但是仍然需要为各种数据结构分配内存,例如物理页面分配器。
一种名为memblock的专门分配器执行引导时间内存管理。架构特定的初始化必须在setup_arch()中设置它,并在mem_init()函数中拆除它。
一旦早期内存管理可用,它提供了各种用于内存分配的函数和宏。分配请求可以指向第一个(可能也是唯一的)节点,或者指向NUMA系统中的特定节点。有一些API变体在分配失败时会触发panic,而另一些则不会。
Memblock还提供了各种控制其行为的API。
Memblock概述
Memblock是在通常的内核内存分配器尚未启动运行时管理内存区域的一种方法。
Memblock将系统内存视为连续区域的集合。这些集合有几种类型:
-
memory - 描述内核可用的物理内存;这可能与系统中实际安装的物理内存不同,例如当内存受到mem=命令行参数的限制时
-
reserved - 描述已分配的区域
-
physmem - 描述引导期间实际可用的物理内存,而不考虑可能的限制和内存热插拔;physmem类型仅在某些架构上可用。
每个区域由struct memblock_region表示,该结构定义了区域的范围、属性和NUMA系统上的NUMA节点ID。每种内存类型由struct memblock_type描述,其中包含一组内存区域以及分配器元数据。"memory"和"reserved"类型都很好地封装在struct memblock中。此结构在构建时静态初始化。区域数组最初大小为INIT_MEMBLOCK_MEMORY_REGIONS(对于“memory”)和INIT_MEMBLOCK_RESERVED_REGIONS(对于“reserved”)。"physmem"的区域数组最初大小为INIT_PHYSMEM_REGIONS。memblock_allow_resize()允许在添加新区域时自动调整区域数组的大小。应谨慎使用此功能,以免为区域数组分配的内存与应保留的区域重叠,例如initrd。
早期架构设置应该通过使用memblock_add()或memblock_add_node()函数告诉memblock物理内存布局是什么。第一个函数不会将区域分配给NUMA节点,适用于UMA系统。但是,也可以在NUMA系统上使用它,并在设置过程的后期使用memblock_set_node()将区域分配给NUMA节点。memblock_add_node()直接执行此类分配。
一旦设置了memblock,就可以使用以下API变体之一来分配内存:
-
memblock_phys_alloc*() - 这些函数返回分配内存的物理地址
-
memblock_alloc*() - 这些函数返回分配内存的虚拟地址。
请注意,这两种API变体都对允许的内存范围和回退方法进行了隐含假设。有关更详细的描述,请参阅memblock_alloc_internal()和memblock_alloc_range_nid()函数的文档。
随着系统引导的进行,特定于架构的mem_init()函数将所有内存释放给伙伴页分配器。
除非架构启用CONFIG_ARCH_KEEP_MEMBLOCK,否则在系统初始化完成后,memblock数据结构(除了“physmem”)将被丢弃。
函数和结构
以下是memblock数据结构、函数和宏的描述。其中一些实际上是内部的,但由于它们已经有文档记录,因此省略它们会显得愚蠢。此外,阅读内部函数的描述可以帮助理解底层发生了什么。
https://www.kernel.org/doc/html/v6.6/core-api/boot-time-mm.html#c.memblock_flags