圖解Linux內(nèi)存管理:伙伴系統(tǒng)
大家好,這里是物聯(lián)網(wǎng)心球。
本文我們繼續(xù)來介紹Linux內(nèi)存管理,今天要講的是內(nèi)存管理中非常重要的機制伙伴系統(tǒng)。
為了小伙伴們能正確理解今天講的內(nèi)容,這里需要強調(diào)一下,我們今天還是圍繞物理內(nèi)存來講解,請不要和虛擬內(nèi)存弄混淆了。
1.伙伴系統(tǒng)是什么?
伙伴系統(tǒng)是一種內(nèi)存管理算法,用于動態(tài)分配和釋放物理內(nèi)存頁。
該算法的核心思想是將相鄰且大小相等的內(nèi)存頁合并成一個大的內(nèi)存頁,從而減少內(nèi)存碎片的產(chǎn)生和浪費。
在伙伴系統(tǒng)中,每個內(nèi)存頁都有一個伙伴,當(dāng)某個內(nèi)存頁被分配時,系統(tǒng)會查找它的伙伴,如果伙伴也未被分配,則將它們合并成一個更大的內(nèi)存頁。
反之,當(dāng)某個內(nèi)存頁被釋放時,系統(tǒng)會查找它的伙伴,如果伙伴也空閑,則將它們合并成一個更大的內(nèi)存頁。
圖片
Linux通過幾個關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)伙伴系統(tǒng):
- pglist_data:表示內(nèi)存節(jié)點,內(nèi)存節(jié)點詳細(xì)介紹請參考文章圖解Linux內(nèi)存管理_整體架構(gòu)。
- zone:表示內(nèi)存區(qū)域,常見的區(qū)域有ZONE_DMA,ZONE_DMA32,ZONE_NORMAL,ZONE_HIGHMEM,每個區(qū)域都對應(yīng)一個伙伴系統(tǒng)。
- free_area:表示分配階級,總共有MAX_ORDER(通常為11)個分配階級,每個分配階級對應(yīng)的內(nèi)存塊大小為2^order個page。
- free_list:表示頁類型鏈表頭,用于內(nèi)存碎片處理,本文不展開討論。
2.內(nèi)存管理區(qū)域
內(nèi)存管理區(qū)域是伙伴系統(tǒng)一個核心的概念,內(nèi)存節(jié)點被進(jìn)一步劃分成更小的內(nèi)存區(qū)域,這個更小的內(nèi)存區(qū)域稱為內(nèi)存管理區(qū)域(zone),zone的定義如下:
圖片
2.1 為什么需要把內(nèi)存節(jié)點劃分成zone?
Linux系統(tǒng)不能把所有的內(nèi)存都按相同的方式去處理,所以把內(nèi)存節(jié)點劃分成不同的區(qū)域,實現(xiàn)內(nèi)存精細(xì)化管理,比如以下幾種情況,需要將物理內(nèi)存進(jìn)行區(qū)域劃分:
- 舊的工業(yè)標(biāo)準(zhǔn)體系結(jié)構(gòu)(Industry Standard Architecture,ISA)總線只能直接訪問 16MB 以下的內(nèi)存,所以需要劃分一個ZONE_DMA區(qū)域來兼容ISA標(biāo)準(zhǔn)。
- X86_32位系統(tǒng)內(nèi)核虛擬地址空間只有1GB,其中896MB空間已經(jīng)用于直接映射,剩余的128M空間需要訪問3GB的物理內(nèi)存空間,所以需要劃分ZONE_HIGHMEM高端內(nèi)存區(qū)域。
2.2 zone類型
zone主要可以劃分為如下幾種類型:
- ZONE_DMA:直接內(nèi)存訪問,是一種允許某些硬件子系統(tǒng)(例如磁盤驅(qū)動器或顯卡)直接訪問系統(tǒng)內(nèi)存的技術(shù)。
- ZONE_DMA32:在32位系統(tǒng)上實現(xiàn)DMA傳輸?shù)囊环N機制,主要用于允許64位或者大于16MB的DMA傳輸。
- ZONE_NORMAL:在Linux內(nèi)存區(qū)域劃分中,表示正常的內(nèi)存區(qū)域,即不包含高端內(nèi)存的區(qū)域。
- ZONE_HIGHMEM:在Linux內(nèi)存區(qū)域劃分中,表示高端內(nèi)存,即不常用的內(nèi)存區(qū)域,通常用于大型數(shù)據(jù)的緩存或者動態(tài)內(nèi)存分配。
Linux內(nèi)存節(jié)點并不一定會包含所有的內(nèi)存區(qū)域類型,多數(shù)情況是只包含部分區(qū)域類型,我們以X86_32和X86_64系統(tǒng)來講解。
X86_32系統(tǒng)物理內(nèi)存劃分如下:
圖片
X86_32系統(tǒng)將物理內(nèi)存劃分為3個zone:
ZONE_DMA:0-16M,DMA內(nèi)存區(qū)域。
ZONE_NORMAL:16M-896M,普通內(nèi)存區(qū)域。
ZONE_HIGHMEM:896M-4GB,高端內(nèi)存只存在于32位系統(tǒng)。
X86_64系統(tǒng)物理內(nèi)存劃分如下:
圖片
X86_64系統(tǒng)將物理內(nèi)存劃分為3個zone:
ZONE_DMA:0-16M,DMA內(nèi)存區(qū)域。
ZONE_DMA32:16M-4GB,DMA32內(nèi)存區(qū)域。
ZONE_NORMAL:4GB-end,普通內(nèi)存區(qū)域。
不同的CPU架構(gòu)和系統(tǒng)支持的內(nèi)存區(qū)域都會有差異,我們可以通過
dmesg | grep "mem"查看Linux系統(tǒng)內(nèi)存區(qū)域的詳細(xì)信息。
圖片
3.伙伴系統(tǒng)初始化
Linux啟動時,伙伴系統(tǒng)并沒有實際物理內(nèi)存,內(nèi)核首先需要根據(jù)系統(tǒng)配置信息初始化各個內(nèi)存區(qū)域,接下來需要將memblock(早期內(nèi)存分配機制)模塊的內(nèi)存釋放至伙伴系統(tǒng),伙伴系統(tǒng)完成初始化后,memblock將退出歷史舞臺,后續(xù)的物理內(nèi)存分配和回收由伙伴系統(tǒng)來完成。
Linux伙伴系統(tǒng)初始化流程如下:
start_kernel()->mm_init()->mem_init()->memblock_free_all()。
Linux內(nèi)核獲取設(shè)備樹物理內(nèi)存信息初始化memblock,由memblock負(fù)責(zé)早期的內(nèi)存分配工作,memblock完成對應(yīng)的工作后,將未使用的內(nèi)存釋放至伙伴系統(tǒng),完成伙伴系統(tǒng)的初始化工作。
圖片
4.伙伴系統(tǒng)內(nèi)存分配
伙伴系統(tǒng)常見的內(nèi)存分配函數(shù)如下:
圖片
內(nèi)存分配函數(shù)需要傳入兩個參數(shù)gfp_mask和order。
- gfp_mask:指定從哪個內(nèi)存區(qū)域分配內(nèi)存,內(nèi)存區(qū)域定義如下:
圖片
- order:指定分配階級。
調(diào)用內(nèi)存分配函數(shù)從伙伴系統(tǒng)分配內(nèi)存時,通過gfg_mask和order找到對應(yīng)的分配階級,并申請2^order個page的內(nèi)存塊。
此時會出現(xiàn)兩種情況:
- 情況1:指定分配階級有空閑內(nèi)存塊。
該情況比較簡單,只需要把該內(nèi)存塊分配出去即可。
- 情況2:指定分配階級沒有空閑內(nèi)存塊。
該情況處理起來會復(fù)雜一點,需要向高階分配階級申請空閑內(nèi)存塊,首先向order+1階申請空閑內(nèi)存塊,如果order+1階沒有空閑內(nèi)存塊,繼續(xù)向order+2階申請空閑內(nèi)存塊,以此類推直到在order+n階找到空閑內(nèi)存塊。
將order+n階空閑內(nèi)存塊減半分裂,其中一半插入上一階內(nèi)存鏈表,另外一半內(nèi)存塊繼續(xù)減半分裂,減半后其中一半內(nèi)存塊繼續(xù)插入對應(yīng)的分配階級內(nèi)存鏈表,另外一半內(nèi)存塊繼續(xù)減半分裂,直至內(nèi)存塊符合申請內(nèi)存塊大小。
圖片
這里介紹一下伙伴系統(tǒng)調(diào)試小技巧,通過cat /proc/buddyinfo可以查看伙伴系統(tǒng)所有分配階級內(nèi)存塊情況。
5.伙伴系統(tǒng)內(nèi)存回收
伙伴系統(tǒng)內(nèi)存回收函數(shù)如下:
圖片
調(diào)用內(nèi)存回收函數(shù)同樣需要指定order,指定order的目的是告知伙伴系統(tǒng)當(dāng)前回收的內(nèi)存塊包含2^order個page。
伙伴系統(tǒng)根據(jù)order找到對應(yīng)的分配階級,伙伴系統(tǒng)不會把回收的內(nèi)存塊直接插入內(nèi)存鏈表,而是先在分配階級內(nèi)存鏈表中查找內(nèi)存塊的伙伴。
如果沒有找到內(nèi)存塊的伙伴,則將內(nèi)存塊插入分配階級內(nèi)存鏈表。
如果找到內(nèi)存塊的伙伴,則將內(nèi)存塊和伙伴進(jìn)行合并,形成一個2倍大小的新的內(nèi)存塊,將新的內(nèi)存塊繼續(xù)插入下一個分配階級內(nèi)存鏈表,同時也需要判斷新的內(nèi)存塊是否存在伙伴,以此類推直到未找到內(nèi)存塊伙伴,并將合并后的內(nèi)存塊插入到最終分配階級內(nèi)存鏈表。
圖片
總結(jié)
伙伴系統(tǒng)是Linux內(nèi)存管理的一個重要機制,伙伴系統(tǒng)通過伙伴機制將小塊的內(nèi)存合并,在一定程度上減少了內(nèi)存碎片,同時也實現(xiàn)了分配連續(xù)物理內(nèi)存的功能。