常用知識 linux核心中常見的記憶體分配方法

2021-08-08 08:59:48 字數 4318 閱讀 6699

linux核心中常見記憶體分配函式

linux核心中採用了一種同時適用於32位和64位系統的記憶體分頁模型,對於32位系統來說,兩級頁表足夠用了,而在x86_64系統中,用到了四級頁表,如圖2-1所示。四級頁表分別為:

    頁全域性目錄(page global directory)

    頁上級目錄(page upper directory)

    頁中間目錄(page middle directory)

    頁表(page table)

頁全域性目錄包含若干頁上級目錄的位址,頁上級目錄又依次包含若干頁中間目錄的位址,而頁中間目錄又包含若干頁表的位址,每乙個頁表項指向乙個頁框。linux中採用4kb大小的頁框作為標準的記憶體分配單元。

多級分頁目錄結構

在實際應用中,經常需要分配一組連續的頁框,而頻繁地申請和釋放不同大小的連續頁框,必然導致在已分配頁框的記憶體塊中分散了許多小塊的空閒頁框。這樣,即使這些頁框是空閒的,其他需要分配連續頁框的應用也很難得到滿足。

為了避免出現這種情況,linux核心中引入了夥伴系統演算法(buddy system)。把所有的空閒頁框分組為11個塊鍊錶,每個塊鍊錶分別包含大小為1,2,4,8,16,32,64,128,256,512和1024個連續頁框的頁框塊。最大可以申請1024個連續頁框,對應4mb大小的連續記憶體。每個頁框塊的第乙個頁框的實體地址是該塊大小的整數倍。

假設要申請乙個256個頁框的塊,先從256個頁框的鍊錶中查詢空閒塊,如果沒有,就去512個頁框的鍊錶中找,找到了則將頁框塊分為2個256個頁框的塊,乙個分配給應用,另外乙個移到256個頁框的鍊錶中。如果512個頁框的鍊錶中仍沒有空閒塊,繼續向1024個頁框的鍊錶查詢,如果仍然沒有,則返回錯誤。

頁框塊在釋放時,會主動將兩個連續的頁框塊合併為乙個較大的頁框塊。

slab分配器源於 solaris 2.4 的分配演算法,工作於物理記憶體頁框分配器之上,管理特定大小物件的快取,進行快速而高效的記憶體分配。

slab分配器為每種使用的核心物件建立單獨的緩衝區。linux 核心已經採用了夥伴系統管理物理記憶體頁框,因此 slab分配器直接工作於夥伴系統之上。每種緩衝區由多個 slab 組成,每個 slab就是一組連續的物理記憶體頁框,被劃分成了固定數目的物件。根據物件大小的不同,預設情況下乙個 slab 最多可以由 1024個頁框構成。出於對齊等其它方面的要求,slab 中分配給物件的記憶體可能大於使用者要求的物件實際大小,這會造成一定的記憶體浪費。

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

__get_free_pages函式是最原始的記憶體分配方式,直接從夥伴系統中獲取原始頁框,返回值為第乙個頁框的起始位址。__get_free_pages在實現上只是封裝了alloc_pages函式,從**分析,alloc_pages函式會分配長度為1size_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巨集定義,在預設的2.6.18核心版本中,該巨集定義為5,於是一次最多能申請1<<5 * 4kb也就是128kb的連續物理記憶體。分析核心原始碼發現,kmem_cache_create函式的size引數大於128kb時會呼叫bug()。測試結果驗證了分析結果,用kmem_cache_create分配超過128kb的記憶體時使核心崩潰。

void *kmalloc(size_t size, gfp_t flags)

kmalloc是核心中最常用的一種記憶體分配方式,它通過呼叫kmem_cache_alloc函式來實現。kmalloc一次最多能申請的記憶體大小由include/linux/kmalloc_size.h的內容來決定,在預設的2.6.18核心版本中,kmalloc一次最多能申請大小為131702b也就是128kb位元組的連續物理記憶體。測試結果表明,如果試圖用kmalloc函式分配大於128kb的記憶體,編譯不能通過。

void *vmalloc(unsigned long size)

前面幾種記憶體分配方式都是物理連續的,能保證較低的平均訪問時間。但是在某些場合中,對記憶體區的請求不是很頻繁,較高的記憶體訪問時間也可以接受,這是就可以分配一段線性連續,物理不連續的位址,帶來的好處是一次可以分配較大塊的記憶體。圖3-1表示的是vmalloc分配的記憶體使用的位址範圍。vmalloc對一次能分配的記憶體大小沒有明確限制。出於效能考慮,應謹慎使用vmalloc函式。在測試過程中,最大能一次分配1gb的空間。

linux核心部分記憶體分布

void *dma_alloc_coherent(struct device *dev, size_t size,

ma_addr_t *dma_handle, gfp_t gfp)

dma是一種硬體機制,允許外圍裝置和主存之間直接傳輸io資料,而不需要cpu的參與,使用dma機制能大幅提高與裝置通訊的吞吐量。dma操作中,涉及到cpu快取記憶體和對應的記憶體資料一致性的問題,必須保證兩者的資料一致,在x86_64體系結構中,硬體已經很好的解決了這個問題, dma_alloc_coherent和__get_free_pages函式實現差別不大,前者實際是呼叫__alloc_pages函式來分配記憶體,因此一次分配記憶體的大小限制和後者一樣。__get_free_pages分配的記憶體同樣可以用於dma操作。測試結果證明,dma_alloc_coherent函式一次能分配的最大記憶體也為4m。

void * ioremap (unsigned long offset, unsigned long size)

ioremap是一種更直接的記憶體「分配」方式,使用時直接指定物理起始位址和需要分配記憶體的大小,然後將該段物理位址對映到核心位址空間。ioremap用到的實體地址空間都是事先確定的,和上面的幾種記憶體分配方式並不太一樣,並不是分配一段新的物理記憶體。ioremap多用於裝置驅動,可以讓cpu直接訪問外部裝置的io空間。ioremap能對映的記憶體由原有的物理記憶體空間決定,所以沒有進行測試。

如果要分配大量的連續物理記憶體,上述的分配函式都不能滿足,就只能用比較特殊的方式,在linux核心引導階段來預留部分記憶體。

void* alloc_bootmem(unsigned long size)

可以在linux核心引導過程中繞過夥伴系統來分配大塊記憶體。使用方法是在linux核心引導時,呼叫mem_init函式之前用alloc_bootmem函式申請指定大小的記憶體。如果需要在其他地方呼叫這塊記憶體,可以將alloc_bootmem返回的記憶體首位址通過export_symbol匯出,然後就可以使用這塊記憶體了。這種記憶體分配方式的缺點是,申請記憶體的**必須在鏈結到核心中的**裡才能使用,因此必須重新編譯核心,而且記憶體管理系統看不到這部分記憶體,需要使用者自行管理。測試結果表明,重新編譯核心後重啟,能夠訪問引導時分配的記憶體塊。

在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時,預留一段記憶體,核心看不見

小於物理記憶體大小,記憶體管理要求較高

注:表中提到的最大記憶體資料來自centos5.3 x86_64系統,其他系統和體系結構會有不同

常用知識 linux核心中常見的記憶體分配方法

linux核心中常見記憶體分配函式 linux核心中採用了一種同時適用於32位和64位系統的記憶體分頁模型,對於32位系統來說,兩級頁表足夠用了,而在x86 64系統中,用到了四級頁表,如圖2 1所示。四級頁表分別為 頁全域性目錄 page global directory 頁上級目錄 page u...

Linux核心中常見記憶體分配函式

linux核心中採用了一種同時適用於32位和64位系統的記憶體分頁模型,對於32位系統來說,兩級頁表足夠用了,而在x86 64系統中,用到了四級頁表,如圖2 1所示。四級頁表分別為 l 頁全域性目錄 page global directory l 頁上級目錄 page upper directory...

Linux核心中常見記憶體分配函式

linux核心中常見記憶體分配函式 linux核心中採用了一種同時適用於32位和64位系統的記憶體分頁模型,對於32位系統來說,兩級頁表足夠用了,而在x86 64系統中,用到了四級頁表,如圖2 1所示。四級頁表分別為 頁全域性目錄 page global directory 頁上級目錄 page u...