1. 程式人生 > >分配記憶體相關函式 >>Linux裝置驅動程式

分配記憶體相關函式 >>Linux裝置驅動程式

據離職開始自學轉行到現在已經有3個月的時間,理解了為啥只有少部分願意去理解核心,不是沒有原因的;
承受著多種壓力和快節奏的生活,讓人們越來越趨向於即時反饋,這樣其實並不是很好;
有段可以安安靜靜自我提升的時間簡直就是奢望,比起他們來說我已經很幸運了;
看了加布里爾·穆奇諾的《當幸福來敲門》,才知道我這點困難算什麼?唯有繼續才能對得起這珍貴的時間;

文章目錄

[0x100]內容概述

  1. 連續的直接對映記憶體塊分配
  2. 連續的直接對映記憶體頁分配
  3. 連續的完全虛擬記憶體頁分配
  4. 引導時分配實體記憶體頁

[0x110] 常見的記憶體區域

  • 記憶體區域型別 : DMA區、常規記憶體區、高階記憶體區,其中高階記憶體的搜尋的範圍最大;
  • 預設標識位設定 :預設搜尋 常規記憶體區、DMA記憶體區;
  • DMA標識位置位 :只搜尋DMA記憶體,通常為RAM的前16M,可用於裝置實體記憶體訪問;
  • HIGHMEM置位 :搜尋所有的三種區域來獲取地址;

[0x200] 核心分配記憶體函式

[0x210] 連續分配直接對映實體記憶體

#include <linux/slab.h>
/*implement kernel-dir/mm/slab.c*/
static __always_inline void *kmalloc(size_t size, gfp_t flags);

功能 : 從低端記憶體高速分配,連續、不清空資料、直接對映實體記憶體;
args 1 : 需要分配的連續實體記憶體大小,位元組為單位;
args 2 : 用於控制記憶體分配的操作方式,記憶體屬性標識
retval : 分配成功的實體記憶體起始地址,如分配失敗 NULL

[0x211] 常見的記憶體標誌

#include <linux/gfp.h> 
//分配標識組合屬性
#define __GFP_IO        ((__force gfp_t)___GFP_IO)      /* 允許啟動 物理 IO */
#define __GFP_FS        ((__force gfp_t)___GFP_FS)      /* 允許呼叫低等級的檔案系統*/
#define __GFP_WAIT      ((__force gfp_t)___GFP_WAIT)    /* 允許等待或者重新排程 */
#define __GFP_HIGH      ((__force gfp_t)___GFP_HIGH)    /* 允許啟用緊急池 消耗最後一個空頁面*/
//分配標識
#define GFP_NOIO        (__GFP_WAIT)                        /*禁止啟動物理IO初始化*/
#define GFP_NOFS        (__GFP_WAIT | __GFP_IO)             /*禁止呼叫低階檔案系統,可休眠,可初始化物理IO*/  
#define GFP_KERNEL      (__GFP_WAIT | __GFP_IO | __GFP_FS)  /*GFP_NOFS | __GFP_FS 可休眠 的呼叫低階檔案系統*/
#define GFP_ATOMIC      (__GFP_HIGH)                        /*原子分配記憶體,允許訪問最後一個可用記憶體頁*/

[0x212] 分配記憶體規則

  1. 在核心中分配記憶體是基於物件塊為單位,所以不像虛擬記憶體可以任意分割大小,最大有可能所需大小的兩倍;
  2. 最小的記憶體大小是32k或者64k,kmalloc單次最大分配不能超過128k;

[0x220] 後備快取記憶體管理–struct kmem_cache

[0x221] 常見 SLAB flags

  • 功能 :用於指導如何分配快取記憶體,或者記憶體分配debug;
#include <linux/slab.h>
#define SLAB_HWCACHE_ALIGN      0x00002000UL    /* 基於快取記憶體行對齊分配的記憶體物件,基於空間換取效率 */
#define SLAB_CACHE_DMA          0x00004000UL    /* 使用DMA區域記憶體分配 */
#define SLAB_PANIC              0x00040000UL    /* 分配失敗發出核心恐慌提示 */

[0x222] 分配快取記憶體物件

#include <linux/slab.h>
/*implement/kernel-3.4.39/mm/slab.c*/  
struct kmem_cache *
kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *))

功能 : 建立快取記憶體物件;
args 1 : 在/proc/slabinfo 檔案中標註當前建立的快取記憶體的名稱;
args 2 : 在當前快取中分配不同記憶體物件區域的大小,均來自於這個值;
args 3 : 記憶體物件對齊方式;
args 4 : SLAB flags;
args 5 :指定記憶體物件的構建方式;
retval : 分配成功返回指向快取的結構體指標,如分配失敗 NULL;

[0x223] 記憶體物件的分配與釋放

#include <linux/slab.h>
/*implement/kernel-3.4.39/mm/slab.c*/  
/*從分配的快取記憶體物件中獲取記憶體物件,通常直接使用 kmalloc 來呼叫這個函式,不直接使用*/
void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
/*返還記憶體物件到快取記憶體中,通常直接使用 kfree 來呼叫這個函式,不直接使用*/
void kmem_cache_free(struct kmem_cache *, void *);

[0x224] 釋放快取記憶體物件

#include <linux/slab.h>
/*implement/kernel-3.4.39/mm/slab.c*/  
/*移除一個快取物件,呼叫前必須確保該快取物件中的記憶體物件都已經返回到快取物件中*/
void kmem_cache_destroy(struct kmem_cache *cachep)

[0x230] 分配直接對映實體記憶體頁–struct page

  1. 高速、連續分配直接對映實體記憶體頁;
  2. 以頁為基本分配記憶體的顆粒細度,節省記憶體空間;
  3. 可以自主調整頁表來合併多個記憶體頁,獲得更大的線性空間;

[0x231] 獲取記憶體頁

#include <linux/gfp.h> 
/*implement kernel-dir/mm/page_alloc.c*/
/*分配單頁記憶體並填0*/
unsigned long get_zeroed_page(gfp_t gfp_mask)
{
  return __get_free_pages(gfp_mask | __GFP_ZERO, 0);
}
/*分配單頁記憶體不填0*/
#define __get_free_page(gfp_mask) __get_free_pages((gfp_mask), 0)

[0x232] 獲取多個記憶體頁

#include <linux/gfp.h> 
/*implement kernel-dir/mm/page_alloc.c*/
/*分配多頁記憶體*/
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
        struct page *page;
        VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);

        page = alloc_pages(gfp_mask, order);
        if (!page)
                return 0;
        return (unsigned long) page_address(page);
}

功能 : 連續分配2次冪數量多個記憶體頁物件,分配的頁數可以在/proc/buddyinfo;
args 1 : slab flags
args 2 : order 標識 以2為底的對數,所以 0 = 1頁,2=4頁;
retval : 頁起始位置,失敗返回0;

[0x233] 釋放記憶體頁

/*銷燬一個記憶體頁,是個呼叫多個記憶體頁的巨集*/
#define free_page(addr) free_pages((addr), 0)
/*銷燬多個記憶體頁*/
void free_pages(unsigned long addr, unsigned int order)
{
        if (addr != 0) {
                VM_BUG_ON(!virt_addr_valid((void *)addr));
                __free_pages(virt_to_page((void *)addr), order);
        }
}

[0x234]頁分配器函式介面

#include <linux/mm_types.h>
#include <linux/gfp.h>
  
/*對外分配記憶體頁操作的巨集介面*/
#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
#define alloc_pages(gfp_mask, order) alloc_pages_node(numa_node_id(), gfp_mask, order)
/*分配記憶體頁核心函式 返回頁結構*/
static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,unsigned int order)
{               
        /* Unknown node is current node */
        if (nid < 0)
                nid = numa_node_id();

        return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));
}
/*銷燬記憶體頁*/
#define __free_page(page) __free_pages((page), 0)
void __free_pages(struct page *page, unsigned int order);
void free_hot_cold_page(struct page *page, int cold);
void free_hot_cold_page_list(struct list_head *list, int cold);

[0x240] 分配完全虛擬記憶體頁–vmalloc

  1. 分配連續的虛擬記憶體頁,相應實體記憶體頁不連續;
  2. vmalloc 的開銷大於任何直接對映物理空間的分配函式,因為需要建立頁表來對映虛擬記憶體區域;
  3. 分配的記憶體只在處理器MMU控制範圍內有效;

[0x241]vmalloc 虛擬記憶體頁地址範圍

#include <asm/pgtable.h>
#define VMALLOC_OFFSET          (8*1024*1024)
#define VMALLOC_START           (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_END     0xff000000UL

[0x242]vmalloc 函式介面

/*基於虛擬記憶體頁為單位 分配連續的虛擬記憶體*/
void *vmalloc(unsigned long size);
/*基於虛擬記憶體頁為單位 分配連續的虛擬記憶體,清空分配頁上資料填0*/
void *vzalloc(unsigned long size);
/*登出虛擬儲存頁*/
void vfree(const void *addr);

[0x250] 引導時分配更大快取

1.只連結入核心的驅動程式;
2. 系統啟動前,獲取更大的連續直接對映實體記憶體的方式;

#define alloc_bootmem(x) __alloc_bootmem(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
#define alloc_bootmem_pages(x) __alloc_bootmem(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
#define alloc_bootmem_low(x) __alloc_bootmem_low(x, SMP_CACHE_BYTES, 0)
#define alloc_bootmem_low_pages(x) __alloc_bootmem_low(x, PAGE_SIZE, 0)

void * __init __alloc_bootmem(unsigned long size, unsigned long align,unsigned long goal)
{
        unsigned long limit = 0;
        return ___alloc_bootmem(size, align, goal, limit);
}
void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,unsigned long goal)
{
        return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
}