歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核中常見內存分配函數介紹

Linux內核中常見內存分配函數介紹

日期:2017/3/2 16:32:45   编辑:Linux內核

1.原理說明

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

  * 頁全局目錄(Page Global Directory)

  * 頁上級目錄(Page Upper Directory)

  * 頁中間目錄(Page Middle Directory)

  * 頁表(Page Table)

  頁全局目錄包含若干頁上級目錄的地址,頁上級目錄又依次包含若干頁中間目錄的地址,而頁中間目錄又包含若干頁表的地址,每一個頁表項指 向一個頁框。Linux中采用4KB大小的 頁框作為標准的內存分配單元。

  多級分頁目錄結構

  1.1.伙伴系統算法

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

  為了避免出現這種情況,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個頁 框的鏈表查找,如果仍然沒有,則返回錯誤。

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

  1.2.slab分 配器

  slab分配器源於 Solaris 2.4 的 分配算法,工作於物理內存頁框分配器之上,管理特定大小對象的緩存,進行快速而高效的內存分配。

  slab分配器為每種使用的內核對象建立單獨的緩沖區。Linux 內核已經采用了伙伴系統管理物理內存頁框,因此 slab分配器直接工作於伙伴系 統之上。每種緩沖區由多個 slab 組成,每個 slab就是一組連續的物理內存頁框,被劃分成了固定數目的對象。根據對象大小的不同,缺省情況下一個 slab 最多可以由 1024個頁框構成。出於對齊 等其它方面的要求,slab 中分配給對象的內存可能大於用戶要求的對象實際大小,這會造成一定的 內存浪費。

  2.常用內存分配函數

  2.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的 連續頁框塊。order參數的最大值由include/Linux/Mmzone.h文 件中的MAX_ORDER宏決定,在默認的2.6.18內 核版本中,該宏定義為10。也就是說在理論上__get_free_pages函 數一次最多能申請1<<10 * 4KB也就是4MB的 連續物理內存。但是在實際應用中,很可能因為不存在這麼大量的連續空閒頁框而導致分配失敗。在測試中,order為10時分配成功,order為11則返回錯誤。

  2.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宏 定義,在默認的2.6.18內核版本中,該宏定義為5, 於是一次最多能申請1<<5 * 4KB也就是128KB的 連續物理內存。分析內核源碼發現,kmem_cache_create函數的size參數大於128KB時會調用BUG()。測試結果驗證了分析結果,用kmem_cache_create分 配超過128KB的內存時使內核崩潰。

  2.3.kmalloc

  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的內存,編譯不能通過。

  2.4.vmalloc

  void *vmalloc(unsigned long size)

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

  Linux內核部分內存分布

  2.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高速緩 存和對應的內存數據一致性的問題,必須保證兩者的數據一致,在x86_64體系結構中,硬件已經很 好的解決了這個問題, dma_alloc_coherent和__get_free_pages函數實現差別不大,前者實際是調用__alloc_pages函 數來分配內存,因此一次分配內存的大小限制和後者一樣。__get_free_pages分配的內 存同樣可以用於DMA操作。測試結果證明,dma_alloc_coherent函 數一次能分配的最大內存也為4M。

  2.6.ioremap

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

  ioremap是一種更直接的內存“分配”方式,使用時直接指定物理起始地址和需要分配內存的大小,然後將該段 物理地址映射到內核地址空間。ioremap用到的物理地址空間都是事先確定的,和上面的幾種內存 分配方式並不太一樣,並不是分配一段新的物理內存。ioremap多用於設備驅動,可以讓CPU直接訪問外部設備的IO空間。ioremap能映射的內存由原有的物理內存空間決定,所以沒有進行測試。

  2.7.Boot Memory

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

  2.7.1.在內核引導時分配內存

  void* alloc_bootmem(unsigned long size)

  可以在Linux內核引導過程中繞過伙伴系統來分配大塊內存。使用方法是在Linux內核引導時,調用mem_init函數之前 用alloc_bootmem函數申請指定大小的內存。如果需要在其他地方調用這塊內存,可以將alloc_bootmem返回的內存首地址通過EXPORT_SYMBOL導 出,然後就可以使用這塊內存了。這種內存分配方式的缺點是,申請內存的代碼必須在鏈接到內核中的代碼裡才能使用,因此必須重新編譯內核,而且內存管理系統 看不到這部分內存,需要用戶自行管理。測試結果表明,重新編譯內核後重啟,能夠訪問引導時分配的內存塊。

  2.7.2.通過內核引導參數預留頂部內存

  在Linux內核引導時,傳入參數“mem=size”保留頂部的內存區間。比如系統有256MB內 存,參數“mem=248M”會預留頂部的8MB內存,進入系統後可以調用ioremap(0xF800000,0x800000)來申請這段內存。
3.幾種分配函數的比較

分配原理

最大內存

其他

__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時,預留一段內存,內核看不見

小於物理內存大小,內存管理要求較高

Copyright © Linux教程網 All Rights Reserved