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

5.伙伴系統內存回收
伙伴系統內存回收函數如下:
圖片
調用內存回收函數同樣需要指定order,指定order的目的是告知伙伴系統當前回收的內存塊包含2^order個page。
伙伴系統根據order找到對應的分配階級,伙伴系統不會把回收的內存塊直接插入內存鏈表,而是先在分配階級內存鏈表中查找內存塊的伙伴。
如果沒有找到內存塊的伙伴,則將內存塊插入分配階級內存鏈表。
如果找到內存塊的伙伴,則將內存塊和伙伴進行合并,形成一個2倍大小的新的內存塊,將新的內存塊繼續插入下一個分配階級內存鏈表,同時也需要判斷新的內存塊是否存在伙伴,以此類推直到未找到內存塊伙伴,并將合并后的內存塊插入到最終分配階級內存鏈表。
圖片
總結
伙伴系統是Linux內存管理的一個重要機制,伙伴系統通過伙伴機制將小塊的內存合并,在一定程度上減少了內存碎片,同時也實現了分配連續物理內存的功能。

























