內核早期內存分配器:memblock
Linux內核使用夥伴系統管理內存,那麼在夥伴系統工做前,如何管理內存?答案
是memblock。
memblock在系統啓動階段進行簡單的內存管理,記錄物理內存的使用狀況。
在進一步介紹memblock以前,有必要先了解下系統內存的使用狀況:
首先,內存中的某些部分是永久的分配給內核的,好比內核代碼段和數據段,ramdisk和fdt佔
用的空間等,它們是系統內存的一部分,可是不能被侵佔,也不參與內存分配,稱之爲靜態內存
;其次,GPU,Camera等都須要預留大量連續內存,這部份內存平時不用,可是系統必須提早預
留好,稱之爲預留內存;最後,內存的其他部分稱之爲動態內存,是須要內核管理的寶貴資源。
memblock把物理內存劃分爲若干內存區,按使用類型分別放在memory和reserved兩個集
合(數組)中,memory即動態內存的集合,reserved集合包括靜態內存和預留內存。
1. memblock關鍵數據結構
memblock數據結構定義以下:
memblock相關數據結構十分的簡單,內核還爲memblock定義了一個全局變量,併爲其賦
初值,以下:
memory類型的內存集合指向memblock_memory_init_regions數組,最多能夠記錄128個內
存區。
reserved類型的內存集合指向memblock_reserved_init_regions數組,最多能夠記錄128個內
存區。
注:內核代碼常常用到相似」__initdata_memblock」的宏定義,一般用來指定變量或函數所在
的section,該宏的定義以下:
2. memblock基本操做
1) 添加內存區
分別爲memory和reserved集合添加內存區,若是新加入的內存區與原有內存區重疊,則合併
到原有內存區,不然插入新內存區。
實際工做由memblock_add_range()完成,type參數指定內存集合類型。
須要注意的是該函數內部會執行兩次:
第一次計算須要插入幾個內存區,若是超過容許的最大內存區個數,則double內存區數組;
第二次執行內存區的實際插入與合併操做。
2) 移除內存區
從memory集合移除給定物理地址所指的內存區,若是是內存區域的一部分,則涉及到調
更多精彩攻略訪問gl.baidu.com 1
整region大小,或者將一個region拆分紅兩個region。
系統將不會爲移除的內存區創建內存映射,這部份內存區後續應該由DMA或CMA管理。
3) 分配內存
使用該函數向kernel申請一塊可用的物理內存,memblock使用自頂向下(取決於bottom_up
的值)的方式查找空閒內存,實際操做是在memory region中查找合適的內存,並加入到reserved
region中以標記這塊內存已經被使用。
4) 釋放內存
使用該函數來釋放由memblock_alloc申請到的物理內存。
3. 探測系統可用內存
內核是如何知曉物理內存的拓撲結構呢?相信不少人都有過相似的疑問。
經過DDR的模式寄存器(MR8),能夠很容易得到內存密度,進而推斷出內存容量,這部分工
做一般由bootloader完成,而後使用fdt或者atag等方式傳遞給內核。
以fdt爲例,內核解析memory節點,取得物理內存的拓撲結構(起始地址及大小),並添加
到memblock中,代碼以下:
該函數掃描memory節點,並解析reg屬性,注意此時DeviceTree尚未執行unflattern操做,
須要使用」fdt」類型接口解析dtb。
以4G DDR爲例,輸出的調試信息以下:
reg屬性由addr和size組成,分別佔用2個cell(u32類型數據),上面的reg data能夠當作:「0
00000080 0 00000080, 01000000 0 0 00557e」。
dtb使用big endian方式存儲數據,須要轉換成cpu字節序。
解析出來的內存包含兩個Rank,起始地址分別是0x80000000和0x100000000,這是系統的
可用內存,用來初始化memory region。
從fdt解析的內存信息是否可信呢?內核有本身的判斷,在啓動階段,內核會根據自身的運行
地址計算內存基地址,即PHYS_OFFSET。
若是base地址小於phys_offset,則內核使用可信的phys_offset作爲主存的基地址。
這裏要注意區分PHYS_OFFSET, PAGE_OFFSET:
PAGE_OFFSET是內核虛擬地址空間的起始地址,PHYS_OFFSET是RAM在物理空間的起
始地址,內核空間的地址映射一般具備固定的偏移量,即:
4. 記錄系統預留內存
這裏說的系統預留內存,包括靜態內存(內核Image,ramdisk,fdt等佔用空間),以及系統
爲Camera,Display等子系統預留的大量連續內存。
更多精彩攻略訪問gl.baidu.com 2
另外,高通平臺一般包含多核,還須要爲Modem,TZ/TA等預留運行空間,這部分空間相似
靜態內存,都是永久分配給其它核心使用,根據節點屬性,一般由DMA管理。
arm64_memblock_init()函數初始化系統預留內存,代碼以下:
「no-map」屬性決定向reserved region添加內存區,仍是從memory region移除內存區,兩者差
別在於內核不會給」no-map」屬性的內存區創建內存映射,即該內存區不在動態內存管理範圍。
預留內存還會被添加到reserved_mem數組,爲後續的初始化作準備,」reg」屬性指定內存區的
起始地址和大小,若是沒有」reg」屬性,還須要爲內存區分配空間。
至此,memblock的初始化工做已經基本完成了,主要是記錄系統內存的使用狀況:
memory region記錄系統了全部可用的動態內存;
reserved region記錄了系統預留內存,這部份內存一般由CMA管理,也屬於動態內存範疇;
reserved_mem數組則記錄系統全部預留內存,包括」no-map」屬性的內存區,爲後續進一步初
始化工做作準備。
5. 初始化預留內存區
內存向來是系統的寶貴資源,預留內存若是僅作爲子系統的專用內存,就有點浪費了。
Linux內核引入CMA(Contiguous Memory Allocator,連續內存分配器)。
其工做原理是:爲驅動預留一段內存,當驅動不用時,Memory Allocator(Buddy System)
能夠分配給用戶進程使用;而當驅動須要使用時,就將進程佔用的內存經過回收或者遷移的方式
騰出來,供驅動使用。
可是並非全部的預留內存都由CMA管理,像Modem,TA等永久分配給其它核心使用的內
存空間,內核並不爲這部分空間創建內存映射,而是交由DMA管理。
經過上面的分析,咱們看到全部預留內存信息都記錄在reserved_mem數組,下面先看看該結
構體的定義:
reserved-memory子節點包含預留內存屬性,典型定義以下:
「reg」和」no-map」屬性前面介紹過,詳細能夠參考reserved-memory.txt,」compatible」屬性使用
標準定義,內核註冊兩種不一樣的處理方法:
「removed-dma-pool」表示該內存區位於DMA管理區,內核不可見(沒有頁表)。
「shared-dma-pool」表示該內存區位於CMA管理區,平時是可用的,只有須要時才分配給驅動
使用。
若是沒有」reg」屬性,即沒有指定預留內存的起始地址,則須要由系統分配預留內存,而後初
始化reserved_mem的ops成員:
以」shared-dma-pool」爲例,它的初始化函數以下:
更多精彩攻略訪問gl.baidu.com 3
此處爲」shared-dma-pool」類型的內存註冊操做方法,cma_init_reserved_mem初始
化cma_area(CMA管理區)。
reserved_mem的ops成員包括init和release兩個操做方法:
驅動註冊預留內存區時調用device_init方法,爲設備指定預留內存操做方法(DMA)或預留內存
區域(CMA),這些方法包括預留內存的申請,釋放和mmap等。
6. 連續內存分配器(CMA)
CMA的初始化必須在buddy系統工做以前和memblock分配器初始化完成以後。 在ARM中,
初始化CMA的接口是:
@limit: 指定CMA區域的上限,在64位系統上@limit的值一般是0x100000000。
以命令行參數」cma=32M@0-0xfffffff」爲例: size_cmdline = 32M, base_cmdline = 0x0,
limit_cmdline = 0xffffffff 。
計算好CMA的size等值之後就進入cma_declare_contiguous中:
dma_contiguous_default_are是用戶自定義CMA管理區,定義以下:
下面進入cma_init_reserved_mem初始化用戶自定義CMA管理區:
以上只是將CMA區域預留下來,並記錄到相關數組,進一步初始化和使用須要等slab等子系
統初始化完成後了。
CMA並不直接開放給驅動開發人員,在註冊設備時可使用」memory-region」屬性指定要操做
的內存區域,須要分配DMA內存時,調用DMA相關函數就能夠了。
例如dma_alloc_coherent(),最終DMA相關的分配函數會到達CMA的分配函
數dma_alloc_from_contiguous()。
7. 進階閱讀
memblock和bootmem的區別CMA(連續內存分配器)Contiguous Memory Allocator (CMA)
源碼分析Linux內核最新連續內存分配器(CMA) — 避免預留大塊內存內存管理筆記(CMA)
更多精彩攻略訪問gl.baidu.com 4數組