常用核心記憶體分配函數
1.__get_free_pages
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
__get_free_pages函數是最原始的記憶體分配方式,直接從夥伴系統中獲取原始頁框,返回值為第一個頁框的起始地址。
__get_free_pages在實現上只是封裝了alloc_pages函數,從代碼分析,alloc_pages函數會分配長度為1< order參數的最大值由include/Linux/Mmzone.h文件中的MAX_ORDER所定義。
也就是說在理論上__get_free_pages函數一次最多能申請1< 但是在實際應用中,很可能因為不存在這麼大量的連續空閒頁框而導致分配失敗。
2.kmem_cache_alloc
struct kmem_cache *kmem_cache_create(const char *name, size_t size,
size_t align, unsigned long flags,
void (*ctor)(void*, struct kmem_cache *, unsigned long),
void (*dtor)(void*, struct kmem_cache *, unsigned long))
void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)
kmem_cache_create/ kmem_cache_alloc是基於slab分配器的一種記憶體分配方式,適用於反復分配釋放同一大小記憶體塊的場合。
首先用kmem_cache_create創建一個高速緩存區域,然後用kmem_cache_alloc從 該高速緩存區域中獲取新的記憶體塊。
kmem_cache_alloc一次能分配的最大記憶體由mm/slab.c文件中的MAX_OBJ_ORDER所定義。於是一次最多能申請1< 分析核心原始碼發現,kmem_cache_create函數的size參數大於128KB時會呼叫BUG()。
測試結果驗證了分析結果,用kmem_cache_create分配超過128KB的記憶體時使核心崩潰。
3.kmalloc
void *kmalloc(size_t size, gfp_t flags)
kmalloc是核心中最常用的一種記憶體分配方式,它通過呼叫kmem_cache_alloc函數來實現。
kmalloc一次最多能申請的記憶體大小由include/Linux/Kmalloc_size.h的內容來決定,
kmalloc一次最多能申請大小為131702B也就是128KB的連續物理記憶體。
4.vmalloc
void *vmalloc(unsigned long size)
前面幾種記憶體分配方式都是物理連續的,能保證較低的平均訪問時間。
但是在某些場合中,對記憶體區的請求不是很頻繁,較高的記憶體訪問時間也可以接受,
這樣就可以分配一段線性連續,物理不連續的地址,帶來的好處是一次可以分配較大塊的記憶體。
vmalloc對一次能分配的記憶體大小沒有明確限制。出於性能考慮,應謹慎使用vmalloc函數。
5.dma_alloc_coherent
void *dma_alloc_coherent(struct device *dev, size_t size, ma_addr_t *dma_handle, gfp_t gfp)
DMA是一種硬體機制,允許外部設備和核心之間直接傳輸IO數據,而不需要CPU的參與。
使用DMA機制能大幅提高與設備通信的吞吐量。
DMA操作中,涉及到CPU高速緩衝和對應的記憶體數據一致性的問題,必須保證兩者的數據一致。
dma_alloc_coherent和__get_free_pages函數實現差別不大。
前者實際是呼叫__alloc_pages函數來分配記憶體,因此一次分配記憶體的大小限制和後者一樣。
__get_free_pages分配的記憶體同樣可以用於DMA操作。
6.ioremap
void * ioremap (unsigned long offset, unsigned long size)
ioremap是一種更直接的記憶體分配方式,使用時直接指定物理起始地址和需要分配記憶體的大小,
然後將該段物理地址映射到核心地址空間。
ioremap用到的物理地址空間都是事先確定的,和上面的幾種記憶體分配方式並不太一樣。
並不是分配一段新的物理記憶體。
ioremap多用於設備驅動,可以讓CPU直接訪問外部設備的IO空間。
ioremap能映射的記憶體由原有的物理記憶體空間決定,所以沒有進行測試。
7.Boot Memory
如果要分配大量的連續物理記憶體,上述的分配函數都不能滿足,就只能用比較特殊的方式,在Linux核心引導階段來預留部分記憶體。
7.1.在核心引導時分配記憶體
void* alloc_bootmem(unsigned long size)
可以在Linux核心引導過程中繞過夥伴系統來分配大塊記憶體。
使用方法是在Linux核心引導時,呼叫mem_init函數之前用alloc_bootmem函數申請指定大小的記憶體。
如果需要在其他地方呼叫這塊記憶體,可以將alloc_bootmem返回的記憶體首地址通過EXPORT_SYMBOL導出,然後就可以使用這塊記憶體了。
這種記憶體分配方式的缺點是,申請記憶體的代碼必須在鏈接到核心中的代碼裏才能使用,因此必須重新編譯核心。
而且記憶體管理系統看不到這部分記憶體,需要用戶自行管理。
7.2.通過核心引導參數預留頂部記憶體
在Linux核心引導時,傳入參數mem=size保留頂部的記憶體區間。
比如系統有256MB內 存,參數mem=248M會預留頂部的8MB記憶體,
進入系統後可以呼叫ioremap(0xF800000,0x800000)來申請這段記憶體。
分配原理 最大記憶體 其他
__get_free_pages 直接對頁框進行操作 4MB 適用於分配較大量的連續物理記憶體
kmem_cache_alloc 基於slab機制實現 128KB 適合需要頻繁申請釋放相同大小記憶體塊時使用
kmalloc 基於kmem_cache_alloc實現 128KB 最常見的分配方式,需要小於頁框大小的記憶體時可以使用
vmalloc 建立非連續物理記憶體到虛擬地址的映射 物理不連續,適合需要大記憶體,但是對地址連續性沒有要求的場合
dma_alloc_coherent 基於__alloc_pages實現 4MB 適用於DMA操作
ioremap 實現已知物理地址到虛擬地址的映射 適用於物理地址已知的場合,如設備驅動
alloc_bootmem 在啟動kernel時,預留一段記憶體,核心看不見 小於物理記憶體大小,記憶體管理要求較高