一文細(xì)說引導(dǎo)內(nèi)存分配器
linux內(nèi)存三大分配器:引導(dǎo)內(nèi)存分配器,伙伴分配器,slab分配器
一、引導(dǎo)內(nèi)存分配器
1.引導(dǎo)內(nèi)存分配器的作用因為內(nèi)核里面有很多內(nèi)存結(jié)構(gòu)體,不可能在靜態(tài)編譯階段就靜態(tài)初始化所有的這些內(nèi)存結(jié)構(gòu)體。另外,在系統(tǒng)啟動過程中,系統(tǒng)啟動后的物理內(nèi)存分配器本身也需要初始化,如伙伴分配器,那么伙伴分配器如何獲取內(nèi)存來初始化自己呢 ?為了達(dá)到這個目標(biāo),我們先實現(xiàn)一個滿足要求的但是可能效率不高的笨家伙,引導(dǎo)內(nèi)存分配器。用它來負(fù)責(zé)系統(tǒng)初始化初期的內(nèi)存管理, 最重要的, 用它來初始化我們內(nèi)存的數(shù)據(jù)結(jié)構(gòu), 直到我們真正的內(nèi)存管理器被初始化完成并能投入使用, 我們將舊的內(nèi)存管理器丟掉。
2.引導(dǎo)內(nèi)存分配器的原理在Linux內(nèi)核中使用struct bootmem_data來描述一個引導(dǎo)內(nèi)存分配,其節(jié)點(diǎn)結(jié)構(gòu)下的一個成員,也就是說每一個節(jié)點(diǎn)都有一個引導(dǎo)內(nèi)存分配。 引導(dǎo)內(nèi)存分配使用struct bootmem_data結(jié)構(gòu)中的node_bootmem_map這個bitmap來呈現(xiàn)memory的狀態(tài),一個bit代表一個物理頁框,也就是用struct page,如果一個bit為1,表示該page已經(jīng)被分配了,如果bit是0,則表示該page未被分配。為了能夠滿足比一個page還小的內(nèi)存塊的分配,引導(dǎo)內(nèi)存分配器會使用last_pos來記住上次分配所使用的PFN以及上次分配所使用的page內(nèi)的偏移:last_offset,下次分配的時候結(jié)合last_pos和last_offset將細(xì)小的內(nèi)存塊分配盡量集中在相同的page中。
3引導(dǎo)內(nèi)存分配器的缺點(diǎn)盡管引導(dǎo)內(nèi)存分配器不會造成嚴(yán)重的內(nèi)存碎片,但是每次分配過程需要線性掃描搜索內(nèi)存來滿足當(dāng)前的分配。因為是檢查bitmap,所以代價比較昂貴,尤其是最先適配(first fit)算法傾向?qū)⑿K內(nèi)存放置在物理內(nèi)存開頭,但是這些內(nèi)存區(qū)域在分配大塊內(nèi)存時,也需要掃描,所以該過程十分浪費(fèi)。所以早期內(nèi)存分配器在系統(tǒng)啟動后就被棄用的原因。
4.bootmem和memblock的比較但是bootmem也有很多問題. 最明顯的就是外碎片的問題, 因此內(nèi)核維護(hù)了memblock內(nèi)存分配器, 同時用memblock實現(xiàn)了一份bootmem相同的兼容API, 即nobootmem, Memblock以前被定義為Logical Memory Block( 邏輯內(nèi)存塊),但根據(jù)Yinghai Lu的補(bǔ)丁, 它被重命名為memblock. 并最終替代bootmem成為初始化階段的內(nèi)存管理器。 bootmem是通過位圖來管理,位圖存在地地址段, 而memblock是在高地址管理內(nèi)存, 維護(hù)兩個鏈表, 即memory和reserved。 memory鏈表維護(hù)系統(tǒng)的內(nèi)存信息(在初始化階段通過bios獲取的), 對于任何內(nèi)存分配, 先去查找memory鏈表, 然后在reserve鏈表上記錄(新增一個節(jié)點(diǎn),或者合并) bootmem和memblock都是就近查找可用的內(nèi)存, bootmem是從低到高找, memblock是從高往低找。 在boot傳遞給kernel memory bank相關(guān)信息后,kernel這邊會以memblcok的方式保存這些信息,當(dāng)伙伴系統(tǒng)沒有起來之前,在內(nèi)核中也是要有一套機(jī)制來管理memory的申請和釋放。linux內(nèi)核可以通過宏定義選擇nobootmem 或者bootmem 來在伙伴起來之前管理內(nèi)存。這兩種機(jī)制對提供的API是一致的,因此對用戶是透明的
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ??


5.bootmem小分析bootmem結(jié)構(gòu)體位于文件include/linux/bootmem.h:
bootmem接口函數(shù): 1)bootmem分配內(nèi)存函數(shù):alloc_bootmem 2)bootmem釋放內(nèi)存函數(shù):free_bootmem
6.memblock結(jié)構(gòu)解析memblock結(jié)構(gòu)體位于include/linux/memblock.h文件:
memblock體系的結(jié)構(gòu):

7.memblock接口函數(shù)解析1)memblock添加內(nèi)存區(qū)域函數(shù):
我們繼續(xù)追memblock_add_range:
2)memblock刪除內(nèi)存區(qū)域函數(shù):memblock_remove
memblock_remove_range:
3)memblock分配內(nèi)存函數(shù):memblock_alloc
4)memblock釋放內(nèi)存函數(shù):memblock_free
7.memblock啟動流程 1)解析設(shè)備樹中的/memory,把所有物理內(nèi)存添加到memblock 2)在memblock_init中初始化memblock linux啟動從init/main.c文件的start_kernel函數(shù)開始,然后從文件setup_arch(arch/arm64/kernel/setup.c文件中)函數(shù)檢測處理器類型,初始化處理器和內(nèi)存,其中的arm64_memblock_init(arch/arm64/mm/init.c文件中)函數(shù)就是arm64架構(gòu)的memblock初始化流程。
最后,引導(dǎo)內(nèi)存分配器退休,會將物理內(nèi)存填充到伙伴分配器中,移交給伙伴分配器進(jìn)行管理。
原文作者:人人極客社區(qū)
