先上基礎,下圖是Linux的記憶體映射模型 1. 每一個進程都有自己的進程空間,進程空間的0 3G是用戶空間,3G 4G是內核空間 2. 每個進程的用戶空間不在同一個物理記憶體頁,但是所有的進程的內核空間對應同樣的物理地址 3. vmalloc分配的地址可以高端記憶體,也可以是低端記憶體 4. 0 896M ...
先上基礎,下圖是Linux的記憶體映射模型
- 每一個進程都有自己的進程空間,進程空間的0-3G是用戶空間,3G-4G是內核空間
- 每個進程的用戶空間不在同一個物理記憶體頁,但是所有的進程的內核空間對應同樣的物理地址
- vmalloc分配的地址可以高端記憶體,也可以是低端記憶體
- 0-896MB的物理地址是線性映射到物理映射區的。
- 內核參數和系統頁表都在TEXT_OFFSET保存,除了進程除了訪問自身的用戶空間對應的DRAM記憶體頁外,都要經過內核空間,也就是都要切換到內核態
記憶體動態申請
和應用層一樣,內核程式也需要動態的分配記憶體,不同的是,內核進程可以控制分配的記憶體是在用戶空間還是內核空間,前者可以用於給用戶空間的堆區分配記憶體,eg,用戶進程的用戶空間的malloc最終就會通過系統調用回調內核空間的記憶體分配函數,此時該記憶體分配函數就屬於該用戶進程,可以給在該用戶進程的堆區分配空間並返回,最終使得一個用會進程在自己的用戶空間獲得記憶體分配;後者只在內核空間分配,所以用戶進程不能直接訪問該空間,所以多用在滿足內核程式自身的記憶體需求,下麵是Linux內核空間申請記憶體常用API:
kmalloc - kfree
kmalloc申請的記憶體在物理記憶體上是連續的,他們與真實的物理地址只有一個固定的偏移,因此存在簡單的轉換關係。這個API 多用來申請不到一個page大小的記憶體。kmalloc的底層需要調用__get_free_pages,參數中表示記憶體類型的gtp_t flags正是這個函數的縮寫,常用的記憶體類型有GFP_USER,GFP_KERNEL,GFP_ATOMIC幾種。
GFP_USER表示為用戶空間頁分配記憶體,可以阻塞;
- GFP_KERNEL是最常用的flag,註意,使用這個flag來申請記憶體時,如果暫時不能滿足,會引起進程阻塞,So,一定不要在中斷處理函數,tasklet和內核定時器等非進程上下文中使用GFP_KERNEL!!!
GFP_ATOMIC就可以用於上述三種情境,這個flag表示如果申請的記憶體不能用,則立即返回。
/**
* kmalloc - allocate memory
* @size: how many bytes of memory are required.
* @flags: the type of memory to allocate.
* The @flags argument may be one of:
* %GFP_USER - Allocate memory on behalf of user. May sleep.
* %GFP_KERNEL - Allocate normal kernel ram. May sleep.
* %GFP_ATOMIC - Allocation will not sleep. May use emergency pools.
*
* For example, use this inside interrupt handlers.
*/
void *kmalloc(size_t size, gfp_t flags);
/**
* kfree - free previously allocated memory
* @objp: pointer returned by kmalloc.
* If @objp is NULL, no operation is performed.
*/
void kfree(const void *objp);
同系列API還有
void *kzalloc(size_t size, gfp_t flags)
__get_free_pages - free_pages
__get_free_pages()與kmalloc()一樣是物理連續的記憶體,這一系列函數是Linux內核中最底層的用於獲取空閑記憶體的方法,因為底層的buddy演算法都是以(2^n)×PAGE_SIZE來管理記憶體的,所以他們總是以頁為單位分配記憶體的
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
同系列API還有
unsigned long __get_free_page(gfp_t gfp)
unsigned long get_zeroed_page(gfp_t gfp_mask)
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
void free_page(unsigned long addr)
vmalloc - vfree
vmalloc在虛擬記憶體空間給出一塊連續的記憶體區,實質上,這片連續的虛擬記憶體在物理記憶體中並不一定連續,所以vmalloc申請的虛擬記憶體和物理記憶體之間也就沒有簡單的換算關係,正因如此,vmalloc()通常用於分配遠大於__get_free_pages()的記憶體空間,它的實現需要建立新的頁表,此外還會調用使用GFP_KERN的kmalloc,so,一定不要在中斷處理函數,tasklet和內核定時器等非進程上下文中使用vmalloc!
/**
* vmalloc - allocate virtually contiguous memory
* @size: allocation size
* Allocate enough pages to cover @size from the page level allocator and map them into contiguous kernel virtual space.
*/
void *vmalloc(unsigned long size)
/**
* vfree - release memory allocated by vmalloc()
* @addr: memory base address
*/
void vfree(const void *addr)
同系列的API還有
/**
* vmalloc_32 - allocate virtually contiguous memory (32bit addressable)
* @size: allocation size
* Allocate enough 32bit PA addressable pages to cover @size from the page level allocator and map them into contiguous kernel virtual space.
*/
void *vmalloc_32(unsigned long size)
slab緩存
我們知道,頁是記憶體映射的基本單位,但內核中很多頻繁創建的對象所需記憶體都不到一頁,此時如果仍然按照頁映射的方式,頻繁的進行分配和釋放就會造成資源的浪費,同時也會降低系統性能。為瞭解決的這樣的問題,內核引入了slab機制,使對象在前後兩次被使用時被分配在同一塊記憶體或同一類記憶體空間,且保留了基本的數據結構,就可以大大提高效率。kmalloc的底層即是使用slab演算法管理分配的記憶體的。註意,slab依然是以頁為單位進行映射,只是映射之後分割這些頁為相同的更小的單元,從而節省了記憶體。slab分配的單元不能小於32B或大於128K。
/**
* kmem_cache_create - 創建slab緩存對象
* @name:slab緩存區名字,
* @size:slab分配的緩存區的每一個單元的大小
* @align:緩存區記憶體的對齊方式,一般給0
* @flags:控制分配的位掩碼,
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5) to catch references to uninitialised memory.
* %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check for buffer overruns.
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware cacheline. This can be beneficial if you're counting cycles as closely as davem.
* %SLAB_CACHE_DMA - Use GFP_DMA memory
* %SLAB_STORE_USER - Store the last owner for bug hunting
*define SLAB_PANIC - Panic if kmem_cache_create() fails
*/
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,unsigned long flags, void (*ctor)(void *))
/**
* kmem_cache_alloc - Allocate an object from this cache.
* @cachep: The cache to allocate from.
* @flags: See kmalloc().
* The flags are only relevant if the cache has no available objects.
*/
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
/**
* kmem_cache_free - Deallocate an object
* @cachep: The cache the allocation was from.
* @objp: The previously allocated object.
* Free an object which was previously allocated from this cache.
*/
void kmem_cache_free(struct kmem_cache *cachep, void *objp)
void kmem_cache_destroy(struct kmem_cache *s)
範例
//創建slab對象
struct kmem_cache_t *xj_sbcache;
xj_sbcache = kmem_cache_create("xjslab",sizeof(struct xj_unit_t),0,SLAB_CACHE_DMA|SLAB_PANIC,NULL,NULL);
//分配slab緩存
struct xj_unit_t *xj_unit;
xj_unit = kmem_cache_alloc(xj_sbcache,GFP_KERNEL);
/* 使用slab緩存 */
/* 釋放slab緩存 */
kmem_cache_free(xj_sbcache, xj_unit);
/* 銷毀slab緩存 */
kmem_cache_destroy(xj_sbcache);
記憶體池
除了slab機制,內核還提供了傳統的記憶體池機制來管理小塊記憶體的分配。記憶體池主要是用來解決可能出現的記憶體不足的情況,因為一個記憶體池在創建的時候就已經分配好了一記憶體,當我們用mempool_alloc向一個已經創建好的記憶體池申請申請記憶體時,該函數首先會嘗試回調記憶體池創建時的分配記憶體函數,如果已經沒有記憶體可以分配,他就會使用記憶體池創建時預先分配的記憶體,這樣就可以避免因為無記憶體分配而陷入休眠,當然,如果預分配的記憶體也已經使用完畢,還是會陷入休眠。slab機制的目的是提高記憶體使用率以及記憶體管理效率,記憶體池的目的是避免記憶體的分配失敗。下麵是內核中提供的關於記憶體池的API
/**
* mempool_create - create a memory pool
* @min_nr: the minimum number of elements guaranteed to be allocated for this pool.
* @alloc_fn: user-defined element-allocation function.
* @free_fn: user-defined element-freeing function.
* @pool_data: optional private data available to the user-defined functions.
*
* this function creates and allocates a guaranteed size, preallocated memory pool. The pool can be used from the mempool_alloc() and mempool_free() functions.
* This function might sleep. Both the alloc_fn() and the free_fn() functions might sleep - as long as the mempool_alloc() function is not called from IRQ contexts.
*/
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data)
/**
* mempool_alloc - allocate an element from a specific memory pool
* @pool: pointer to the memory pool which was allocated via mempool_create().
* @gfp_mask: the usual allocation bitmask.
* this function only sleeps if the alloc_fn() function sleeps or returns NULL. Note that due to preallocation, this function never* fails when called from process contexts. (it might fail if called from an IRQ context.)
*/
void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
/**
* mempool_free - return an element to the pool.
* @element: pool element pointer.
* @pool: pointer to the memory pool which was allocated via mempool_create().
*
* this function only sleeps if the free_fn() function sleeps.
*/
void mempool_free(void *element, mempool_t *pool)
/**
* mempool_destroy - deallocate a memory pool
* @pool: pointer to the memory pool which was allocated via mempool_create().
*
* Free all reserved elements in @pool and @pool itself. This function only sleeps if the free_fn() function sleeps.
*/
void mempool_destroy(mempool_t *pool)