
register_page_bootmem_info是 Linux 內(nèi)核啟動階段的一個初始化函數(shù)用于向 bootmem 分配器注冊每個 NUMA 節(jié)點的元數(shù)據(jù)信息。它位于mm/memory_hotplug.c或arch/x86/mm/init_64.c中是連接 SPARSEMEM 內(nèi)存模型與 bootmem 分配器的關(guān)鍵環(huán)節(jié)。核心作用注冊節(jié)點元數(shù)據(jù)在 SPARSEMEM 模型下每個 NUMA 節(jié)點需要維護一些啟動階段專用的元數(shù)據(jù)例如usemap用于跟蹤 memory section 中哪些頁幀已被使用。pageblock_flags用于記錄 pageblock 的可遷移性類型用于內(nèi)存碎片管理。這些元數(shù)據(jù)在啟動早期由 bootmem 分配器分配但它們的struct page描述符需要被注冊到 bootmem 的引用計數(shù)系統(tǒng)中。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 }條件編譯只有在以下兩種配置之一啟用時這個函數(shù)才有實際工作CONFIG_NUMANUMA 系統(tǒng)需要為每個節(jié)點管理獨立的元數(shù)據(jù)。CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP大頁優(yōu)化 vmemmap 的場景下也需要管理額外的元數(shù)據(jù)。遍歷在線節(jié)點for_each_online_node(i)遍歷系統(tǒng)中所有處于在線狀態(tài)的 NUMA 節(jié)點對每個節(jié)點調(diào)用register_page_bootmem_info_node(NODE_DATA(i))。register_page_bootmem_info_node的工作這個輔助函數(shù)會針對單個節(jié)點注冊以下元數(shù)據(jù)的struct pagenode-node_page節(jié)點自身的struct page。pgdat-node_zones[].usemap每個 zone 的 usemap 頁面。pgdat-node_zones[].pageblock_flagspageblock 標(biāo)志頁面。對于每一個這樣的頁面它會調(diào)用get_page_bootmem()來遞增該頁的引用計數(shù)通過 bootmem 的引用計數(shù)機制確保這些元數(shù)據(jù)在 bootmem 被釋放時不會被錯誤地回收。設(shè)計意圖這個函數(shù)解決了一個生命周期管理問題SPARSEMEM 的元數(shù)據(jù)在啟動早期由 bootmem 分配但它們的生命周期需要與struct page數(shù)組vmemmap保持一致。通過將它們的struct page注冊到 bootmem 的引用計數(shù)系統(tǒng)中內(nèi)核確保了在 bootmem 釋放時這些元數(shù)據(jù)不會被一并釋放從而保證了內(nèi)存管理子系統(tǒng)的穩(wěn)定性。這個函數(shù)是啟動流程中一個“不起眼但關(guān)鍵”的環(huán)節(jié)——沒有它NUMA 系統(tǒng)或大頁優(yōu)化場景下的內(nèi)存管理元數(shù)據(jù)可能會在 bootmem 退出時被錯誤回收導(dǎo)致系統(tǒng)崩潰。bootmem 分配器是 Linux 內(nèi)核在啟動早期、伙伴系統(tǒng)Buddy System尚未就緒時使用的臨時內(nèi)存分配器。它的核心任務(wù)是為內(nèi)核初始化過程提供一套可用的物理內(nèi)存分配機制直到mem_init()被調(diào)用其管理的內(nèi)存被移交給伙伴系統(tǒng)后它便“功成身退”。核心原理位圖與最先適配bootmem 的實現(xiàn)相對簡單直接它基于First Fit最先適配算法并使用一個位圖bitmap來記錄物理頁的分配狀態(tài)。內(nèi)存記錄位圖系統(tǒng)啟動時bootmem 會為管理的物理內(nèi)存區(qū)域分配一個位圖。位圖中的每一位對應(yīng)一個物理頁。1表示該頁已被占用0表示空閑。最先適配算法當(dāng)內(nèi)核申請內(nèi)存時bootmem 會從頭掃描位圖找到第一塊足夠大的連續(xù)空閑頁塊進行分配。這種算法實現(xiàn)簡單但效率不高且容易在物理內(nèi)存中留下“空洞”。核心數(shù)據(jù)結(jié)構(gòu)bootmem_databootmem 為每個 NUMA 節(jié)點維護一個bootmem_data結(jié)構(gòu)體用于記錄該節(jié)點的內(nèi)存管理信息。字段作用node_boot_start該節(jié)點管理的物理內(nèi)存起始地址node_low_pfn該節(jié)點管理的物理內(nèi)存結(jié)束頁幀號node_bootmem_map指向內(nèi)存記錄位圖的指針last_pos/last_offset記錄上一次分配的位置用于加速后續(xù)的小塊內(nèi)存分配支持小于一頁的分配主要接口與工作流程bootmem 提供了一系列以alloc_bootmem和free_bootmem開頭的接口用于內(nèi)存的分配與釋放。初始化在setup_arch()階段架構(gòu)相關(guān)代碼會調(diào)用init_bootmem()將可用物理內(nèi)存注冊到位圖中。預(yù)留內(nèi)存通過reserve_bootmem()將內(nèi)核鏡像、initrd、設(shè)備樹等已占用內(nèi)存區(qū)域標(biāo)記為“已使用”。分配與釋放內(nèi)核在初始化期間通過alloc_bootmem()申請內(nèi)存。值得注意的是bootmem 分配的內(nèi)存默認(rèn)會被清零且如果分配失敗會觸發(fā) panic這在早期啟動階段是合理的策略。交接給伙伴系統(tǒng)當(dāng)內(nèi)核完成基礎(chǔ)初始化調(diào)用mem_init()時free_all_bootmem()會遍歷位圖將所有標(biāo)記為“空閑”的頁釋放給伙伴系統(tǒng)。此后bootmem 便不再管理這些內(nèi)存。歷史與現(xiàn)狀被 memblock 取代bootmem 曾是 Linux 內(nèi)核多年的標(biāo)準(zhǔn)引導(dǎo)分配器但隨著系統(tǒng)復(fù)雜度增加其局限性如對非連續(xù)內(nèi)存支持不佳逐漸暴露。memblock作為更現(xiàn)代、更簡潔的引導(dǎo)內(nèi)存管理器被引入并最終取代了 bootmem。從 Linux 5.x 系列開始bootmem 已被完全移除。為了保持兼容性內(nèi)核曾提供mm/nobootmem.c作為包裝層讓舊的 bootmem API 調(diào)用轉(zhuǎn)向 memblock但如今主流架構(gòu)均已直接使用 memblock。