標籤:
Copyright: 該文章著作權由潘雲登所有。可在非商業目的下任意傳播和複製。
對於商業目的下對本文的任何行為需經作者同意。
kmalloc
|
#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
void kfree(const void *ptr); |
使用kmalloc可以獲得以位元組為單位的一塊核心記憶體,它不對所擷取的記憶體空間清零。這個函數返回一個指向記憶體塊的指標,其記憶體塊至少要有size大小。所分配的記憶體區在物理上是連續的。在出錯時,它返回NULL。因此,在調用kmalloc()後,必須檢查返回的是不是NULL。由kmalloc分配的記憶體,使用kfree方法進行釋放。
kmalloc的第一個參數是要分配塊的大小,第二個參數是分配器標誌。這個標誌在<linux/gfp.h>檔案中聲明,可以分為三類:行為修飾符、區修飾符以及類型標識。行為修飾符表示核心應當如何分配所需的記憶體,主要有:
|
__GFP_WAIT 分配器可以休眠
__GFP_HIGH 分配器可以訪問緊急事件緩衝池
__GFP_IO 分配器可以啟動磁碟I/O
__GFP_FS 分配器可以開機檔案系統I/O |
區修飾符表示從哪裡分配記憶體:可用於DMA的記憶體(__GFP_DMA)、常規記憶體以及高端記憶體(__GFP_HIGHMEM)。類型標誌組合了行為修飾符和區修飾符,將各種可能用到的組合歸納為不同類型,簡化了修飾符的使用。因此,最常使用的flags參數為GFP_KERNEL和GFP_ATOMIC。後者用在中斷處理常式或其它運行於進程上下文之外的代碼中,這時kmalloc方法不會休眠。
|
#include <linux/gfp.h>
#define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
#define GFP_ATOMIC (__GFP_HIGH) |
實際上,kmalloc是建立在後面介紹的後備快取之上的。因此,核心只能分配一些預定義的、固定大小的位元組數組。根據當前體繫結構使用的頁面大小,kmalloc能處理的最小記憶體塊是32或者64。如果希望代碼具有完整的可移植性,則不應該分配大於128KB的記憶體。
樣本:
|
char *buf;
buf = kmalloc(BUF_SIZE, GFP_ATOMIC);
if (!buf)
/* 記憶體配置出錯! */
kfree(buf); |
後備快取
分配和釋放資料結構是所有核心中最普遍的操作之一。為了便於資料的頻繁分配和回收,核心提供了後備快取機制,稱為“slab分配器”。slab分配器實現的快取具有kmem_cache_t類型,可通過調用kmem_cache_create建立。
|
#include <linux/slab.h>
kmem_cache_t *kmem_cache_create(const char *name, size_t size,
size_t offset,
unsigned long flags,
void (*constructor)(void *, kmem_cache_t *, unsigned long flags),
void (*destructor)(void *, kmem_cache_t *, unsigned long flags)); |
參數name存放快取的名字。size是快取中每個元素的大小。offset是快取中第一個對象的位移,用來確保在頁內進行特定的對齊,一般取0。flags是一個位元遮罩,用來控制快取的行為。constructor和destructor為快取的構造和解構函式,分別在新頁追加到快取和從快取中刪去頁時調用,通常取NULL。kmem_cache_create在成功時返回一個指向所建立快取的指標,否則,返回NULL。在建立快取後,使用kmem_cache_alloc從中分配記憶體對象。釋放一個記憶體對象時使用kmem_cache_free。要銷毀一個快取,則調用kmem_cache_destroy。
|
void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
void kmem_cache_free(kmem_cache_t *cache, const void *obj);
int kmem_cache_destroy(kmem_cache_t *cache); |
樣本:
|
kmem_cache_t *task_struct_cachep;
/*建立快取,SLAB_PANIC標誌在分配失敗時提醒slab層,
如果沒有提供SLAB_PANIC 標誌,必須自己檢查傳回值*/
task_struct_cachep = kmem_cache_create("task_struct",
sizeof(struct task_struct),
ARCH_MIN_TASKALIGN,
SLAB_PANIC,
NULL,
NULL);
/*分配記憶體對象*/
struct task_struct *tsk;
tsk = kmem_cache_alloc(task_struct_cachep, GFP_KERNEL);
if (!tsk)
return NULL;
/*釋放記憶體對象*/
kmem_cache_free(task_struct_cachep, tsk);
/*撤銷快取*/
int err;
err = kmem_cache_destroy(task_struct_cachep);
if (err)
/*撤銷快取出錯*/ |
分配頁
如果模組需要分配大塊的記憶體,使用面向頁的分配技術會更好些。分配頁面可以使用下面的函數:
|
#include <linux/gfp.h>
unsigned long get_zeroed_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); |
它們返回記憶體地區第一個位元組的指標。其中,get_zeroed_page將分配所得的分頁清空。參數gfp_mask為分配器標誌,與kmaloc中的一樣。參數order是要分配的頁面數的以2為底的對數。當用位元組表示記憶體大小時,需要使用get_order函數進行轉換,而不應該對頁面大小進行任何假設,如:
|
#include <asm/page.h>
/*驅動程式需要16KB空間*/
/* get_order的參數必須是2的冪*/
int order = get_order(16*1024);
buf = get_free_pages(GFP_KERNEL, order); |
當程式不再需要使用頁面時,可以使用下列函數進行釋放:
|
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order); |
樣本:
|
unsigned long page;
page = __get_free_pages(GFP_KERNEL, 3);
if (!page) {
/*沒有足夠的記憶體,你必須處理這種錯誤! */
return ENOMEM;
}
/* ‘page‘現在指向8個連續頁中第1個頁的地址*/
free_pages(page, 3); |
vmalloc
|
#include <linux/vmalloc.h>
void * vmalloc(unsigned long size);
void vfree(void *addr); |
vmalloc函數的工作方式類似於kmalloc,只不過前者分配的記憶體虛擬位址是連續的,而物理地址則無需連續。它通過分配非連續的實體記憶體塊,再修改頁表,把記憶體映射到邏輯地址空間的連續地區中。通過vmalloc獲得的頁必須一個一個地進行映射,效率不高,因此,只在不得已(一般是為了獲得大塊記憶體)時使用。vmalloc函數返回一個指標,指向邏輯上連續的一塊記憶體區,其大小至少為size。在發生錯誤時,函數返回NULL。vmalloc可能睡眠,因此,不能從中斷上下文中進行調用,也不能從其它不允許阻塞的情況下調用。要釋放通過vmalloc所獲得的記憶體,應使用vfree函數。
樣本:
|
char *buf;
buf = vmalloc(16 * PAGE_SIZE); /*獲得16頁*/
if (!buf)
/*錯誤,不能分配記憶體*/
vfree(buf); |
記憶體配置方法 kmalloc()、vmalloc()、__get_free_pages()