您好,登錄后才能下訂單哦!
這篇文章主要介紹“linux內存管理相關的函數有哪些”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“linux內存管理相關的函數有哪些”文章能幫助大家解決問題。
linux內存管理相關的函數:1、kmalloc(),用于內核態的內存分配;2、vmalloc(),一般用在為只存在于軟件中(沒有對應的硬件意義)的較大的順序緩沖區分配內存;3、alloc_page()和alloc_pages()函數,可以在內核空間分配;4、__get_free_pages()系列函數,返回一個或多個頁面的虛擬地址;5、kmem_cache_alloc()等。
kmalloc()函數類似與我們常見的malloc()函數,前者用于內核態的內存分配,后者用于用戶態。
kmalloc()函數在物理內存中分配一塊連續的存儲空間,且和malloc()函數一樣,不會清除里面的原始數據,如果內存充足,它的分配速度很快。其原型如下:
static inline void *kmalloc(size_t size, gfp_t flags); /*返回的是虛擬地址*/
size:待分配的內存大小。由于Linux內存管理機制的原因,內存只能按照頁面大小(一般32位機為4KB,64位機為8KB)進行分配,這樣就導致了當我們僅需要幾個字節內存時,系統仍會返回一個頁面的內存,顯然這是極度浪費的。所以,不同于malloc的是,kmalloc的處理方式是:內核先為其分配一系列不同大小(32B、64B、128B、… 、128KB)的內存池,當需要分配內存時,系統會分配大于等于所需內存的最小一個內存池給它。即kmalloc分配的內存,最小為32字節,最大為128KB。如果超過128KB,需要采樣其它內存分配函數,例如vmalloc()。
flag:該參數用于控制函數的行為,最常用的是GFP_KERNEL,表示當當前沒有足夠內存分配時,進程進入睡眠,待系統將緩沖區中的內容SWAP到硬盤中后,獲得足夠內存后再喚醒進程,為其分配。更多標志見下圖:
使用 GFP_ KERNEL 標志申請內存時,若暫時不能滿足,則進程會睡眠等待頁,即會引起阻塞,因此不能在中斷上下文或持有自旋鎖的時候使用GFP_KERNE 申請內存。所以,在中斷處理函數、tasklet 和內核定時器等非進程上下文中不能阻塞,此時驅動應當使用 GFP_ATOMIC 標志來申請內存。當使用 GFP_ATOMIC 標志申請內存時,若不存在空閑頁,則不等待,直接返回。
除了上述表格所列標志外,還包括如下:
_ _GFP_DMA(要求分配在能夠 DMA 的內存區)
_ _GFP_HIGHMEM(指示分配的內存可以位于高端內存)
_ _GFP_COLD(請求一個較長時間不訪問的頁)
_ _GFP_NOWARN(當一個分配無法滿足時,阻止內核發出警告)
_ _GFP_HIGH(高優先級請求,允許獲得被內核保留給緊急狀況使用的最后的內存頁)
_ _GFP_REPEAT(分配失敗則盡力重復嘗試)
_ _GFP_NOFAIL(標志只許申請成功,不推薦)
_ _GFP_NORETRY(若申請不到,則立即放棄)
使用 kmalloc()申請的內存應使用 kfree()
釋放,這個函數的用法和用戶空間的 free()類似。
vmalloc()
一般用在為只存在于軟件中(沒有對應的硬件意義)的較大的順序緩沖區分配內存,當內存沒有足夠大的連續物理空間可以分配時,可以用該函數來分配虛擬地址連續但物理地址不連續的內存。由于需要建立新的頁表,所以它的開銷要遠遠大于kmalloc及后面將要講到的__get_free_pages()
函數。且vmalloc()
不能用在原子上下文中,因為它的內部實現使用了標志為 GFP_KERNEL 的kmalloc()
。其函數原型如下:
void *vmalloc(unsigned long size); void vfree(const void *addr);
使用 vmalloc 函數的一個例子函數是 create_module()
系統調用,它利用 vmalloc()函數來獲取被創建模塊需要的內存空間。
內存分配是一項要求嚴格的任務,無論什么時候,都應該對返回值進行檢測。
在驅動編程中可以使用copy_from_user()
對內存進行使用。下面舉一個使用vmalloc函數的示例:
static int xxx(...) { ... cpuid_entries = vmalloc(sizeof(struct kvm_cpuid_entry) * cpuid->nent); if(!cpuid_entries) goto out; if(copy_from_user(cpuid_entries, entries, cpuid->nent * sizeof(struct kvm_cpuid_entry))) goto out_free; for(i=0; i<cpuid->nent; i++){ vcpuid->arch.cpuid_entries[i].eax = cpuid_entries[i].eax; ... vcpuid->arch.cpuid_entries[i].index = 0; } ... out_free: vfree(cpuid_entries); out: return r; }
在linux中,內存分配是以頁為單位的,32位機中一頁為4KB,64位機中,一頁為8KB,但具體還有根據平臺而定。
根據返回值類型的不同,頁分配函數分為兩類,一是返回物理頁地址,二是返回虛擬地址。虛擬地址和物理地址起始相差一個固定的偏移量。
#define __pa(x) ((x) - PAGE_OFFSET) static inline unsigned long virt_to_phys(volatile void *address) { return __pa((void *)address); } #define __va(x) ((x) + PAGE_OFFSET) static inline void* phys_to_virt(unsigned long address) { return __va(address); }
根據返回頁面數目分類,分為僅返回單頁面的函數和返回多頁面的函數。
該函數定義在頭文件/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) //分配連續2^order個頁面 static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order) { if(unlikely(order >= MAX_ORDER)) return NULL; if(nid < 0) nid = numa_node_id(); return __alloc_pages(gfp_mask, order, noed_zonelist(nid, gfp_mask)); }
它是kmalloc函數實現的基礎,返回一個或多個頁面的虛擬地址。該系列函數/宏包括 get_zeroed_page()
、_ _get_free_page()
和_ _get_free_pages()
。在使用時,其申請標志的值及含義與 kmalloc()
完全一樣,最常用的是 GFP_KERNEL 和 GFP_ATOMIC。
/*分配多個頁并返回分配內存的首地址,分配的頁數為2^order,分配的頁不清零。 order 允許的最大值是 10(即 1024 頁)或者 11(即 2048 頁),依賴于具體 的硬件平臺。*/ unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) { struct page *page; page = alloc_pages(gfp_mask, order); if(!page) return 0; return (unsigned long)page_address(page); } #define __get_free_page(gfp_mask) __get_free_pages(gfp_mask, 0) /*該函數返回一個指向新頁的指針并且將該頁清零*/ unsigned long get_zeroed_page(unsigned int flags);
使用_ _get_free_pages()
系列函數/宏申請的內存應使用free_page(addr)
或free_pages(addr, order)
函數釋放:
#define __free_page(page) __free_pages((page), 0) #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); } } void __free_pages(struct page *page, unsigned int order) { if(put_page_testzero(page)){ if(order == 0) free_hot_page(page); else __free_pages_ok(page, order); } }
free_pages()函數是調用__free_pages()函數完成內存釋放的。
當在驅動程序中,遇到反復分配、釋放同一大小的內存塊時(例如,inode、task_struct等),建議使用內存池技術(對象在前后兩次被使用時均分配在同一塊內存或同一類內存空間,且保留了基本的數據結構,這大大提高了效率)。在linux中,有一個叫做slab分配器的內存池管理技術,內存池使用的內存區叫做后備高速緩存。
salb相關頭文件在linux/slab.h中,在使用后備高速緩存前,需要創建一個kmem_cache
的結構體。
該函數創建一個slab緩存(后備高速緩沖區),它是一個可以駐留任意數目全部同樣大小的后備緩存。其原型如下:
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 ong)));
其中:
name:創建的緩存名;
size:可容納的緩存塊個數;
align:后備高速緩沖區中第一個內存塊的偏移量(一般置為0);
flags:控制如何進行分配的位掩碼,包括 SLAB_NO_REAP
(即使內存緊缺也不自動收縮這塊緩存)、SLAB_HWCACHE_ALIGN
( 每 個 數 據 對 象 被 對 齊 到 一 個 緩 存 行 )、SLAB_CACHE_DMA
(要求數據對象在 DMA 內存區分配)等);
ctor:是可選的內存塊對象構造函數(初始化函數);
dtor:是可選的內存對象塊析構函數(釋放函數)。
一旦創建完后備高速緩沖區后,就可以調用kmem_cache_alloc()
在緩存區分配一個內存塊對象了,其原型如下:
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);
cachep指向開始分配的后備高速緩存,flags與傳給kmalloc函數的參數相同,一般為GFP_KERNEL。
該函數釋放一個內存塊對象:
void *kmem_cache_free(struct kmem_cache *cachep, void *objp);
與kmem_cache_create
對應的是銷毀函數,釋放一個后備高速緩存:
int kmem_cache_destroy(struct kmem_cache *cachep);
它必須等待所有已經分配的內存塊對象被釋放后才能釋放后備高速緩存區。
創建一個存放線程結構體(struct thread_info)的后備高速緩存,因為在linux中涉及頻繁的線程創建與釋放,如果使用__get_free_page()函數會造成內存的大量浪費,效率也不高。所以在linux內核的初始化階段就創建了一個名為thread_info的后備高速緩存,代碼如下:
/* 創建slab緩存 */ static struct kmem_cache *thread_info_cache; thread_info_cache = kmem_cache_create("thread_info", sizeof(struct thread_info), \ SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); /* 分配slab緩存 */ struct thread_info *ti; ti = kmem_cache_alloc(thread_info_cache, GFP_KERNEL); /* 使用slab緩存 */ ... /* 釋放slab緩存 */ kmem_cache_free(thread_info_cache, ti); kmem_cache_destroy(thread_info_cache);
在 Linux 內核中還包含對內存池的支持,內存池技術也是一種非常經典的用于分配大量小對象的后備緩存技術。
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, \ mempool_free_t *free_fn, void *pool_data);
mempool_create()函數用于創建一個內存池,min_nr 參數是需要預分配對象的數目,alloc_fn 和 free_fn 是指向內存池機制提供的標準對象分配和回收函數的指針,其原型分別為:
typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data);
pool_data 是分配和回收函數用到的指針,gfp_mask 是分配標記。只有當_ _GFP_WAIT
標記被指定時,分配函數才會休眠。
在內存池中分配和回收對象需由以下函數來完成:
void *mempool_alloc(mempool_t *pool, int gfp_mask); void mempool_free(void *element, mempool_t *pool);
mempool_alloc()用來分配對象,如果內存池分配器無法提供內存,那么就可以用預分配的池。
void mempool_destroy(mempool_t *pool);
mempool_create()函數創建的內存池需由 mempool_destroy()來回收。
關于“linux內存管理相關的函數有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。