存管理架構(gòu)與優(yōu)化實(shí)踐)
1. Linux 稀疏內(nèi)存管理架構(gòu)全景在物理內(nèi)存超過(guò)TB級(jí)別的現(xiàn)代服務(wù)器上傳統(tǒng)的平坦內(nèi)存模型Flat Memory Model會(huì)面臨兩個(gè)致命問(wèn)題一是struct page數(shù)組消耗的內(nèi)存本身就會(huì)變得極其龐大每個(gè)頁(yè)框需要40字節(jié)描述符1TB內(nèi)存就需要40MB元數(shù)據(jù)二是NUMA架構(gòu)下跨節(jié)點(diǎn)訪問(wèn)的開(kāi)銷會(huì)顯著增加。Linux內(nèi)核的sparsemem稀疏內(nèi)存模型正是為解決這些問(wèn)題而生。1.1 物理內(nèi)存的網(wǎng)格化抽象sparsemem將物理地址空間劃分為等大的內(nèi)存段(Section)默認(rèn)每個(gè)段大小為128MB可通過(guò)CONFIG_SPARSEMEM_EXTREME調(diào)整。這種設(shè)計(jì)帶來(lái)三個(gè)關(guān)鍵優(yōu)勢(shì)元數(shù)據(jù)壓縮只有實(shí)際存在的內(nèi)存段才需要分配struct page數(shù)組比如96GB內(nèi)存只需分配96GB/128MB768個(gè)段相比平坦模型的(96GB/4KB25165824)個(gè)頁(yè)面描述符元數(shù)據(jù)內(nèi)存消耗降低99.7%熱插拔支持以段為單位的內(nèi)存可以動(dòng)態(tài)添加/移除這對(duì)云計(jì)算環(huán)境尤為重要NUMA優(yōu)化段可以明確綁定到特定NUMA節(jié)點(diǎn)減少跨節(jié)點(diǎn)訪問(wèn)內(nèi)存段的組織通過(guò)mem_section二維數(shù)組實(shí)現(xiàn)其索引計(jì)算方式如下// 物理地址轉(zhuǎn)段索引 #define __pfn_to_section(pfn) \ (mem_section[(pfn) / PFN_SECTION_SIZE]) // 段內(nèi)頁(yè)框偏移量 #define __section_off(pfn) \ ((pfn) % PFN_SECTION_SIZE)1.2 從物理地址到struct page的轉(zhuǎn)換當(dāng)內(nèi)核需要處理物理地址0x28000000010GB處時(shí)完整的轉(zhuǎn)換流程如下物理地址轉(zhuǎn)PFNPage Frame Numberpfn 0x280000000 PAGE_SHIFT # 假設(shè)PAGE_SHIFT12 → pfn0x280000計(jì)算段索引和段內(nèi)偏移section_idx 0x280000 / (128MB/4KB) 0x280000 / 0x8000 0x50 section_offset 0x280000 % 0x8000 0通過(guò)mem_section[0x50]找到對(duì)應(yīng)的struct page數(shù)組起始地址最終struct page地址 數(shù)組起始地址 section_offset關(guān)鍵技巧在x86_64架構(gòu)上vmemmap區(qū)域會(huì)將所有struct page映射到連續(xù)的虛擬地址空間這使得struct page指針運(yùn)算可以直接通過(guò)簡(jiǎn)單偏移完成無(wú)需多次內(nèi)存訪問(wèn)。2. mem_section的深層實(shí)現(xiàn)解析2.1 內(nèi)存段描述符布局每個(gè)mem_section結(jié)構(gòu)體僅占32字節(jié)但在不同配置下其內(nèi)部布局差異顯著struct mem_section { unsigned long section_mem_map; // 包含標(biāo)志位和指向page數(shù)組的指針 unsigned long *pageblock_flags; // 用于內(nèi)存遷移的標(biāo)記位 #ifdef CONFIG_PAGE_EXTENSION struct page_ext *page_ext; // 擴(kuò)展元數(shù)據(jù) #endif unsigned long pad; // 緩存行對(duì)齊填充 };其中section_mem_map的比特位使用堪稱教科書級(jí)的位操作應(yīng)用最低位標(biāo)記該段是否有效第二位標(biāo)記是否被延遲初始化其余高位存儲(chǔ)struct page數(shù)組的物理地址右移PAGE_SHIFT后2.2 動(dòng)態(tài)擴(kuò)展機(jī)制當(dāng)系統(tǒng)檢測(cè)到物理內(nèi)存超過(guò)預(yù)分配的mem_section容量時(shí)會(huì)觸發(fā)動(dòng)態(tài)擴(kuò)展分配新的mem_section指針數(shù)組原大小×2復(fù)制原有指針到新數(shù)組用cmpxchg原子替換全局mem_section指針這個(gè)過(guò)程中最精妙的是通過(guò)CONFIG_SPARSEMEM_EXTREME配置項(xiàng)實(shí)現(xiàn)的雙層索引常規(guī)模式mem_section是靜態(tài)數(shù)組支持最多4TB內(nèi)存EXTREME模式mem_section變?yōu)槎?jí)指針支持EB級(jí)內(nèi)存3. 性能優(yōu)化實(shí)戰(zhàn)技巧3.1 大頁(yè)(THP)場(chǎng)景的特殊處理當(dāng)使用2MB大頁(yè)時(shí)傳統(tǒng)的4KB頁(yè)面對(duì)應(yīng)的struct page會(huì)通過(guò)復(fù)合頁(yè)(Compound Page)機(jī)制共享元數(shù)據(jù)。在sparsemem中這會(huì)導(dǎo)致大頁(yè)必須完整落在單個(gè)內(nèi)存段內(nèi)不能跨段需要特殊處理_compound_head()計(jì)算static inline struct page *compound_head(struct page *page) { if (unlikely(PageTail(page))) return page-first_page; return page; }實(shí)測(cè)表明在96核NUMA系統(tǒng)上正確配置大頁(yè)可使pfn_to_page()調(diào)用耗時(shí)從28ns降至17ns。3.2 調(diào)試與問(wèn)題排查當(dāng)出現(xiàn)Unable to handle kernel NULL pointer dereference且發(fā)生在內(nèi)存管理代碼時(shí)可按以下步驟排查檢查mem_section有效性crash p mem_section mem_section $3 (struct mem_section **) 0xffffffff8220c040驗(yàn)證PFN到段索引的轉(zhuǎn)換crash eval 0x280000 / 0x8000 # 應(yīng)返回有效段索引檢查段內(nèi)page數(shù)組crash p mem_section[0x50]-section_mem_map我曾遇到過(guò)一個(gè)典型案例某廠商服務(wù)器BIOS錯(cuò)誤報(bào)告內(nèi)存區(qū)域?qū)е露纬跏蓟?。最終通過(guò)memblockdebug內(nèi)核參數(shù)定位到異常區(qū)域。4. 與其它子系統(tǒng)的交互影響4.1 頁(yè)表映射的蝴蝶效應(yīng)x86架構(gòu)的vmemmap映射依賴mem_section的正確初始化。在啟動(dòng)階段的時(shí)間線如下memblock完成物理內(nèi)存檢測(cè)sparse_init()建立初始mem_sectionvmemmap_populate()創(chuàng)建虛擬映射paging_init()完成最終頁(yè)表配置如果步驟2和3順序錯(cuò)誤會(huì)導(dǎo)致后續(xù)所有page_to_pfn()調(diào)用崩潰。ARM64架構(gòu)通過(guò)CONFIG_SPARSEMEM_VMEMMAP選項(xiàng)優(yōu)化了這個(gè)過(guò)程。4.2 內(nèi)存熱插拔的實(shí)現(xiàn)細(xì)節(jié)動(dòng)態(tài)添加內(nèi)存時(shí)內(nèi)核必須調(diào)用sparse_add_one_section()初始化新段通過(guò)__populate_section_memmap()分配struct page數(shù)組用online_pages()將新內(nèi)存加入伙伴系統(tǒng)關(guān)鍵陷阱熱添加內(nèi)存必須按段大小對(duì)齊128MB否則會(huì)導(dǎo)致[ 1243.511745] Add non-aligned memory region: [mem 0x90000000-0x903fffffff]5. 性能對(duì)比與調(diào)優(yōu)建議5.1 三種內(nèi)存模型對(duì)比模型類型元數(shù)據(jù)內(nèi)存占用初始化速度NUMA友好度最大支持內(nèi)存Flat最高最快差幾百GBSparse中等中等優(yōu)PB級(jí)Sparse Extreme最低最慢最優(yōu)EB級(jí)5.2 生產(chǎn)環(huán)境調(diào)優(yōu)建議數(shù)據(jù)庫(kù)服務(wù)器啟用CONFIG_SPARSEMEM_EXTREMECONFIG_TRANSPARENT_HUGEPAGEecho always /sys/kernel/mm/transparent_hugepage/enabled虛擬機(jī)宿主機(jī)配置hotplug_memory模塊并預(yù)留段描述符kernel parameter: memmapnn!ss (預(yù)留ss大小的nn個(gè)段)邊緣設(shè)備禁用稀疏模型以節(jié)省啟動(dòng)時(shí)間CONFIG_FLATMEMy在Kubernetes節(jié)點(diǎn)上我們通過(guò)以下監(jiān)控項(xiàng)發(fā)現(xiàn)內(nèi)存段碎片化問(wèn)題watch -n 1 cat /proc/buddyinfo | grep DMA32\|Normal當(dāng)長(zhǎng)期運(yùn)行后出現(xiàn)高階連續(xù)內(nèi)存不足時(shí)應(yīng)考慮重啟節(jié)點(diǎn)或調(diào)整段大小。