先上基礎,下圖是Linux的內(nèi)存映射模型
每一個進程都有自己的進程空間,進程空間的0-3G是用戶空間,3G-4G是內(nèi)核空間
每個進程的用戶空間不在同一個物理內(nèi)存頁,但是所有的進程的內(nèi)核空間對應同樣的物理地址
vmalloc分配的地址可以高端內(nèi)存,也可以是低端內(nèi)存
0-896MB的物理地址是線性映射到物理映射區(qū)的。
內(nèi)存動態(tài)申請
和應用層一樣,內(nèi)核程序也需要動態(tài)的分配內(nèi)存,不同的是,內(nèi)核進程可以控制分配的內(nèi)存是在用戶空間還是內(nèi)核空間,前者可以用于給用戶空間的堆區(qū)分配內(nèi)存,eg,用戶進程的用戶空間的malloc最終就會通過系統(tǒng)調(diào)用回調(diào)內(nèi)核空間的內(nèi)存分配函數(shù),此時該內(nèi)存分配函數(shù)就屬于該用戶進程,可以給在該用戶進程的堆區(qū)分配空間并返回,最終使得一個用會進程在自己的用戶空間獲得內(nèi)存分配;后者只在內(nèi)核空間分配,所以用戶進程不能直接訪問該空間,所以多用在滿足內(nèi)核程序自身的內(nèi)存需求,下面是Linux內(nèi)核空間申請內(nèi)存常用API:
kmalloc - kfree
kmalloc申請的內(nèi)存在物理內(nèi)存上是連續(xù)的,他們與真實的物理地址只有一個固定的偏移,因此存在簡單的轉(zhuǎn)換關系。這個API?多用來申請不到一個page大小的內(nèi)存。kmalloc的底層需要調(diào)用__get_free_pages,參數(shù)中表示內(nèi)存類型的gtp_t flags正是這個函數(shù)的縮寫,常用的內(nèi)存類型有GFP_USER,GFP_KERNEL,GFP_ATOMIC幾種。
GFP_USER表示為用戶空間頁分配內(nèi)存,可以阻塞;
GFP_KERNEL是最常用的flag,注意,使用這個flag來申請內(nèi)存時,如果暫時不能滿足,會引起進程阻塞,So,一定不要在中斷處理函數(shù),tasklet和內(nèi)核定時器等非進程上下文中使用GFP_KERNEL?。?!
GFP_ATOMIC就可以用于上述三種情境,這個flag表示如果申請的內(nèi)存不能用,則立即返回。
/** * 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()一樣是物理連續(xù)的內(nèi)存,這一系列函數(shù)是Linux內(nèi)核中最底層的用于獲取空閑內(nèi)存的方法,因為底層的buddy算法都是以(2^n)×PAGE_SIZE來管理內(nèi)存的,所以他們總是以頁為單位分配內(nèi)存的
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在虛擬內(nèi)存空間給出一塊連續(xù)的內(nèi)存區(qū),實質(zhì)上,這片連續(xù)的虛擬內(nèi)存在物理內(nèi)存中并不一定連續(xù),所以vmalloc申請的虛擬內(nèi)存和物理內(nèi)存之間也就沒有簡單的換算關系,正因如此,vmalloc()通常用于分配遠大于__get_free_pages()的內(nèi)存空間,它的實現(xiàn)需要建立新的頁表,此外還會調(diào)用使用GFP_KERN的kmalloc,so,一定不要在中斷處理函數(shù),tasklet和內(nèi)核定時器等非進程上下文中使用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緩存
我們知道,頁是內(nèi)存映射的基本單位,但內(nèi)核中很多頻繁創(chuàng)建的對象所需內(nèi)存都不到一頁,此時如果仍然按照頁映射的方式,頻繁的進行分配和釋放就會造成資源的浪費,同時也會降低系統(tǒng)性能。為了解決的這樣的問題,內(nèi)核引入了slab機制,使對象在前后兩次被使用時被分配在同一塊內(nèi)存或同一類內(nèi)存空間,且保留了基本的數(shù)據(jù)結(jié)構(gòu),就可以大大提高效率。kmalloc的底層即是使用slab算法管理分配的內(nèi)存的。注意,slab依然是以頁為單位進行映射,只是映射之后分割這些頁為相同的更小的單元,從而節(jié)省了內(nèi)存。slab分配的單元不能小于32B或大于128K。
/** * kmem_cache_create - 創(chuàng)建slab緩存對象 * @name:slab緩存區(qū)名字, * @size:slab分配的緩存區(qū)的每一個單元的大小 * @align:緩存區(qū)內(nèi)存的對齊方式,一般給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)
范例
//創(chuàng)建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);
內(nèi)存池
除了slab機制,內(nèi)核還提供了傳統(tǒng)的內(nèi)存池機制來管理小塊內(nèi)存的分配。內(nèi)存池主要是用來解決可能出現(xiàn)的內(nèi)存不足的情況,因為一個內(nèi)存池在創(chuàng)建的時候就已經(jīng)分配好了一內(nèi)存,當我們用mempool_alloc向一個已經(jīng)創(chuàng)建好的內(nèi)存池申請申請內(nèi)存時,該函數(shù)首先會嘗試回調(diào)內(nèi)存池創(chuàng)建時的分配內(nèi)存函數(shù),如果已經(jīng)沒有內(nèi)存可以分配,他就會使用內(nèi)存池創(chuàng)建時預先分配的內(nèi)存,這樣就可以避免因為無內(nèi)存分配而陷入休眠,當然,如果預分配的內(nèi)存也已經(jīng)使用完畢,還是會陷入休眠。slab機制的目的是提高內(nèi)存使用率以及內(nèi)存管理效率,內(nèi)存池的目的是避免內(nèi)存的分配失敗。下面是內(nèi)核中提供的關于內(nèi)存池的API
評論