linux內存分配方法總結


內存映射結構:

1.32位地址線尋址4G的內存空間,其中0-3G為用戶程序所獨有,3G-4G為內核占有。

2.struct page:整個物理內存在初始化時,每個4kb頁面生成一個對應的struct page結構,這個page結構就獨一無二的代表這個物理內存頁面,並存放在mem_map全局數組中。

3.段式映射:首先根據代碼段選擇子cs為索引,以GDT值為起始地址的段描述表中選擇出對應的段描述符,隨后根據段描述符的基址,本段長度,權限信息等進行校驗,校驗成功后。cs:offset中的32位偏移量直接與本段基址相累加,得出最終訪問地址。

0-3G與mem_map的映射方式:
因linux中采用的段式映射為flat模式,所以從邏輯地址到線性地址沒有變化。從段式出來進入頁式,每個用戶進程都獨自擁有一個頁目錄表(pdt),運行時存放於CR3。  CR3(頁目錄) + 前10位 =>  頁面表基址 + 中10位 => 頁表項 + 后12位 => 物理頁面地址

3G-4G與mem_map的映射方式:
分為三種類型:低端內存/普通內存/高端內存。
低端內存:3G-3G+16M 用於DMA        __pa線性映射
普通內存:3G+16M-3G+896M          __pa線性映射 (若物理內存<896M,則分界點就在3G+實際內存)
高端內存:3G+896-4G               采用動態的分配方式

4.高端內存(假設3G+896為高端內存起址)
作用:訪問到1G以外的物理內存空間。
線性地址共分為三段:vmalloc段/kmap段/kmap_atomic段(針對與不同的內存分配方式)


從內存分配函數的結構來看主要分為下面幾個部分:
a.伙伴算法(最原始的面向頁的分配方式)
alloc_pages 接口:
    struct page * alloc_page(unsigned int gfp_mask)——分配一頁物理內存並返回該頁物理內存的page結構指針。
    struct page * alloc_pages(unsigned int gfp_mask, unsigned int order)——分配 個連續的物理頁並返回分配的第一個物理頁的page結構指針。
    <釋放函數:__free_page(s)>
    
    內核中定義:#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)   
    最終都是調用 __alloc_pages.
    其中MAX_ORDER 11,及最大分配到到頁面個數為2^10(即4M)。
    分配頁后還不能直接用,需要得到該頁對應的虛擬地址:
    void *page_address(struct page *page);
    低端內存的映射方式:__va((unsigned long)(page  -  mem_map)  <<  12)
    高端內存到映射方式:struct page_address_map分配一個動態結構來管理高端內存。(內核是訪問不到vma的3G以下的虛擬地址的) 具體映射由kmap / kmap_atomic執行。
    
get_free_page接口:(alloc_pages接口兩步的替代函數)
    unsigned long get_free_page(unsigned int gfp_mask)
    unsigned long __get_free_page(unsigned int gfp_mask)
    Unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order)
    <釋放函數:free_page>
    與alloc_page(s)系列最大的區別是無法申請高端內存,因為它返回到是一個線性地址,而高端內存是需要額外映射才可以訪問的。

b.slab高速緩存(反復分配很多同一大小內存)   注:使用較少
    kmem_cache_t* xx_cache;
    創建: xx_cache = kmem_cache_create("name", sizeof(struct xx), SLAB_HWCACHE_ALIGN, NULL, NULL);
    分配: kmem_cache_alloc(xx_cache, GFP_KERNEL);
    釋放: kmem_cache_free(xx_cache, addr);
  內存池
      mempool 不使用。
 
c.kmalloc(最常用的分配接口)         注:必須小於128KB
    GFP_ATOMIC 不休眠,用於中斷處理等情況
    GFP_KERNEL 會休眠,一般狀況使用此標記
    GFP_USER   會休眠
    __GFP_DMA  分配DMA內存
    kmalloc/kfree
    
d.vmalloc/vfree
    vmalloc采用高端內存預留的虛擬空間來收集內存碎片引起的不連續的物理內存頁,是用於非連續物理內存分配。
當kmalloc分配不到內存且無物理內存連續的需求時,可以使用。(優先從高端內存中查找)
    
e.ioremap()/iounmap()
  ioremap()的作用是把device寄存器和內存的物理地址區域映射到內核虛擬區域,返回值為內核的虛擬地址。使用的線性地址區間也在vmmlloc段
注:
vmalloc()與 alloc_pages(_GFP_HIGHMEM)+kmap();前者不連續,后者只能映射一個高端內存頁面
__get_free_pages與alloc_pages(NORMAL)+page_address(); 兩者完全等同
內核地址通過 __va/__pa進行中低內存的直接映射
高端內存采用kmap/kmap_atomic的方式來映射

    
個人總結如下:
a.在<128kB的一般內存分配時,使用kmalloc
    允許睡眠:GFP_KERNEL
    不允許睡眠:GFP_ATOMIC
b.在>128kB的內存分配時,使用get_free_pages,獲取成片頁面,直接返回虛擬地址(<4M)(或alloc_pages + page_address)
c.b失敗,
    如果要求分配高端內存:alloc_pages(_GFP_HIGHMEM)+kmap(僅能映射一個頁面)
    如果不要求內存連續: 則使用vmalloc進行分配邏輯連續的大塊頁面.(不建議)/分配速度較慢,訪問速率較慢。
d.頻繁創建和銷毀很多較大數據結構,使用slab.
e.高端內存映射:
    允許睡眠:kmap              (永久映射)
    不允許睡眠:kmap_atomic      (臨時映射)會覆蓋以前到映射(不建議)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM