日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux内存分配器类型,内核早期内存分配器:memblock

發布時間:2024/9/27 linux 63 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内存分配器类型,内核早期内存分配器:memblock 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原標題:內核早期內存分配器:memblock

本文轉載自Linux愛好者

本文來自 程雪濤的自薦投稿

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,該宏的定義如下:

#define __meminitdata __attribute__ ((__section__(".meminit.data")))

2. memblock基本操作1) 添加內存區

分別為memory和reserved集合添加內存區,如果新加入的內存區與原有內存區重疊,則合并到原有內存區,否則插入新內存區。

實際工作由memblock_add_range()完成,type參數指定內存集合類型。

需要注意的是該函數內部會執行兩次:

第一次計算需要插入幾個內存區,如果超過允許的最大內存區個數,則double內存區數組;

第二次執行內存區的實際插入與合并操作。

2) 移除內存區

intmemblock_remove(phys_addr_tbase,phys_addr_tsize);

從memory集合移除給定物理地址所指的內存區,如果是內存區域的一部分,則涉及到調整region大小,或者將一個region拆分成兩個region。

系統將不會為移除的內存區建立內存映射,這部分內存區后續應該由DMA或CMA管理。

3) 分配內存

phys_addr_t memblock_alloc(phys_addr_tsize,phys_addr_talign);

phys_addr_t memblock_alloc_range(phys_addr_tsize,phys_addr_talign,phys_addr_tstart,phys_addr_tend);

使用該函數向kernel申請一塊可用的物理內存,memblock使用自頂向下(取決于bottom_up的值)的方式查找空閑內存,實際操作是在memory region中查找合適的內存,并加入到reserved region中以標記這塊內存已經被使用。

4) 釋放內存

intmemblock_free(phys_addr_tbase,phys_addr_tsize);

使用該函數來釋放由memblock_alloc申請到的物理內存。

3. 探測系統可用內存

內核是如何知曉物理內存的拓撲結構呢?相信很多人都有過類似的疑問。

通過DDR的模式寄存器(MR8),可以很容易獲得內存密度,進而推斷出內存容量,這部分工作通常由bootloader完成,然后使用fdt或者atag等方式傳遞給內核。

以fdt為例,內核解析memory節點,取得物理內存的拓撲結構(起始地址及大小),并添加到memblock中,代碼如下:

setup_arch()->setup_machine_fdt()->early_init_dt_scan()->early_init_dt_scan_memory()

{

......

reg=of_get_flat_dt_prop(node,"reg",&l);

......

while((endp-reg)>=(dt_root_addr_cells+dt_root_size_cells)){

u64base,size;

base=dt_mem_next_cell(dt_root_addr_cells,&reg);

size=dt_mem_next_cell(dt_root_size_cells,&reg);

......

early_init_dt_add_memory_arch(base,size);

}

}

該函數掃描memory節點,并解析reg屬性,注意此時DeviceTree還沒有執行unflattern操作,需要使用”fdt”類型接口解析dtb。

以4G DDR為例,輸出的調試信息如下:

[0.000000]memory scan nodememory,regsize32,data:080080,100000000557e

[0.000000]-80000000,80000000

[0.000000]-100000000,7e550000

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。

voidearly_init_dt_add_memory_arch(base,size)

{

constu64phys_offset=__pa(PAGE_OFFSET);

......

if(base

pr_warning("Ignoring memory range 0x%llx - 0x%llxn",

base,phys_offset);

size-=phys_offset-base;

base=phys_offset;

}

memblock_add(base,size);

}

從fdt解析的內存信息是否可信呢?內核有自己的判斷,在啟動階段,內核會根據自身的運行地址計算內存基地址,即PHYS_OFFSET。

如果base地址小于phys_offset,則內核使用可信的phys_offset做為主存的基地址。

這里要注意區分PHYS_OFFSET, PAGE_OFFSET:

PAGE_OFFSET是內核虛擬地址空間的起始地址,PHYS_OFFSET是RAM在物理空間的起始地址,內核空間的地址映射通常具有固定的偏移量,即:

#define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))

4. 記錄系統預留內存

這里說的系統預留內存,包括靜態內存(內核Image,ramdisk,fdt等占用空間),以及系統為Camera,Display等子系統預留的大量連續內存。

另外,高通平臺通常包含多核,還需要為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數組,下面先看看該結構體的定義:

“reg”和”no-map”屬性前面介紹過,詳細可以參考reserved-memory.txt,”compatible”屬性使用標準定義,內核注冊兩種不同的處理方法:

RESERVEDMEM_OF_DECLARE(dma,"removed-dma-pool",removed_dma_setup);

RESERVEDMEM_OF_DECLARE(cma,"shared-dma-pool",rmem_cma_setup);

“removed-dma-pool”表示該內存區位于DMA管理區,內核不可見(沒有頁表)。

“shared-dma-pool”表示該內存區位于CMA管理區,平時是可用的,只有需要時才分配給驅動使用。

如果沒有”reg”屬性,即沒有指定預留內存的起始地址,則需要由系統分配預留內存,然后初始化reserved_mem的ops成員:

此處為”shared-dma-pool”類型的內存注冊操作方法,cma_init_reserved_mem初始化cma_area(CMA管理區)。

reserved_mem的ops成員包括init和release兩個操作方法:

structreserved_mem_ops{

int(*device_init)(structreserved_mem *rmem,structdevice *dev);

void(*device_release)(structreserved_mem *rmem,structdevice *dev);

};

驅動注冊預留內存區時調用device_init方法,為設備指定預留內存操作方法(DMA)或預留內存區域(CMA),這些方法包括預留內存的申請,釋放和mmap等。

6. 連續內存分配器(CMA)

CMA的初始化必須在buddy系統工作之前和memblock分配器初始化完成之后。

在ARM中,初始化CMA的接口是:

以命令行參數”cma=32M@0-0xfffffff”為例: size_cmdline = 32M, base_cmdline = 0x0, limit_cmdline = 0xffffffff 。

計算好CMA的size等值以后就進入cma_declare_contiguous中:

以上只是將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)返回搜狐,查看更多

責任編輯:

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的linux内存分配器类型,内核早期内存分配器:memblock的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。