init register_page_bootmem_info

发布时间:2026/9/29 20:07:53
init register_page_bootmem_info register_page_bootmem_info是 Linux 内核启动阶段的一个初始化函数用于向 bootmem 分配器注册每个 NUMA 节点的元数据信息。它位于mm/memory_hotplug.c或arch/x86/mm/init_64.c中是连接 SPARSEMEM 内存模型与 bootmem 分配器的关键环节。核心作用注册节点元数据在 SPARSEMEM 模型下每个 NUMA 节点需要维护一些启动阶段专用的元数据例如usemap用于跟踪 memory section 中哪些页帧已被使用。pageblock_flags用于记录 pageblock 的可迁移性类型用于内存碎片管理。这些元数据在启动早期由 bootmem 分配器分配但它们的struct page描述符需要被注册到 bootmem 的引用计数系统中。register_page_bootmem_info正是完成这项工作的入口。代码逻辑解析static void __init register_page_bootmem_info(void) { #if defined(CONFIG_NUMA) || defined(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP) int i; for_each_online_node(i) register_page_bootmem_info_node(NODE_DATA(i)); #endif }条件编译只有在以下两种配置之一启用时这个函数才有实际工作CONFIG_NUMANUMA 系统需要为每个节点管理独立的元数据。CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP大页优化 vmemmap 的场景下也需要管理额外的元数据。遍历在线节点for_each_online_node(i)遍历系统中所有处于在线状态的 NUMA 节点对每个节点调用register_page_bootmem_info_node(NODE_DATA(i))。register_page_bootmem_info_node的工作这个辅助函数会针对单个节点注册以下元数据的struct pagenode-node_page节点自身的struct page。pgdat-node_zones[].usemap每个 zone 的 usemap 页面。pgdat-node_zones[].pageblock_flagspageblock 标志页面。对于每一个这样的页面它会调用get_page_bootmem()来递增该页的引用计数通过 bootmem 的引用计数机制确保这些元数据在 bootmem 被释放时不会被错误地回收。设计意图这个函数解决了一个生命周期管理问题SPARSEMEM 的元数据在启动早期由 bootmem 分配但它们的生命周期需要与struct page数组vmemmap保持一致。通过将它们的struct page注册到 bootmem 的引用计数系统中内核确保了在 bootmem 释放时这些元数据不会被一并释放从而保证了内存管理子系统的稳定性。这个函数是启动流程中一个“不起眼但关键”的环节——没有它NUMA 系统或大页优化场景下的内存管理元数据可能会在 bootmem 退出时被错误回收导致系统崩溃。bootmem 分配器是 Linux 内核在启动早期、伙伴系统Buddy System尚未就绪时使用的临时内存分配器。它的核心任务是为内核初始化过程提供一套可用的物理内存分配机制直到mem_init()被调用其管理的内存被移交给伙伴系统后它便“功成身退”。核心原理位图与最先适配bootmem 的实现相对简单直接它基于First Fit最先适配算法并使用一个位图bitmap来记录物理页的分配状态。内存记录位图系统启动时bootmem 会为管理的物理内存区域分配一个位图。位图中的每一位对应一个物理页。1表示该页已被占用0表示空闲。最先适配算法当内核申请内存时bootmem 会从头扫描位图找到第一块足够大的连续空闲页块进行分配。这种算法实现简单但效率不高且容易在物理内存中留下“空洞”。核心数据结构bootmem_databootmem 为每个 NUMA 节点维护一个bootmem_data结构体用于记录该节点的内存管理信息。字段作用node_boot_start该节点管理的物理内存起始地址node_low_pfn该节点管理的物理内存结束页帧号node_bootmem_map指向内存记录位图的指针last_pos/last_offset记录上一次分配的位置用于加速后续的小块内存分配支持小于一页的分配主要接口与工作流程bootmem 提供了一系列以alloc_bootmem和free_bootmem开头的接口用于内存的分配与释放。初始化在setup_arch()阶段架构相关代码会调用init_bootmem()将可用物理内存注册到位图中。预留内存通过reserve_bootmem()将内核镜像、initrd、设备树等已占用内存区域标记为“已使用”。分配与释放内核在初始化期间通过alloc_bootmem()申请内存。值得注意的是bootmem 分配的内存默认会被清零且如果分配失败会触发 panic这在早期启动阶段是合理的策略。交接给伙伴系统当内核完成基础初始化调用mem_init()时free_all_bootmem()会遍历位图将所有标记为“空闲”的页释放给伙伴系统。此后bootmem 便不再管理这些内存。历史与现状被 memblock 取代bootmem 曾是 Linux 内核多年的标准引导分配器但随着系统复杂度增加其局限性如对非连续内存支持不佳逐渐暴露。memblock作为更现代、更简洁的引导内存管理器被引入并最终取代了 bootmem。从 Linux 5.x 系列开始bootmem 已被完全移除。为了保持兼容性内核曾提供mm/nobootmem.c作为包装层让旧的 bootmem API 调用转向 memblock但如今主流架构均已直接使用 memblock。