溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

linux內(nèi)存管理相關(guān)的函數(shù)有哪些

發(fā)布時(shí)間:2023-04-19 10:11:55 來源:億速云 閱讀:80 作者:iii 欄目:建站服務(wù)器

這篇文章主要介紹“l(fā)inux內(nèi)存管理相關(guān)的函數(shù)有哪些”的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“l(fā)inux內(nèi)存管理相關(guān)的函數(shù)有哪些”文章能幫助大家解決問題。

linux內(nèi)存管理相關(guān)的函數(shù):1、kmalloc(),用于內(nèi)核態(tài)的內(nèi)存分配;2、vmalloc(),一般用在為只存在于軟件中(沒有對應(yīng)的硬件意義)的較大的順序緩沖區(qū)分配內(nèi)存;3、alloc_page()和alloc_pages()函數(shù),可以在內(nèi)核空間分配;4、__get_free_pages()系列函數(shù),返回一個(gè)或多個(gè)頁面的虛擬地址;5、kmem_cache_alloc()等。

1、kmalloc()

kmalloc()函數(shù)類似與我們常見的malloc()函數(shù),前者用于內(nèi)核態(tài)的內(nèi)存分配,后者用于用戶態(tài)。
kmalloc()函數(shù)在物理內(nèi)存中分配一塊連續(xù)的存儲空間,且和malloc()函數(shù)一樣,不會清除里面的原始數(shù)據(jù),如果內(nèi)存充足,它的分配速度很快。其原型如下:

static inline void *kmalloc(size_t size, gfp_t flags);	/*返回的是虛擬地址*/
  • size:待分配的內(nèi)存大小。由于Linux內(nèi)存管理機(jī)制的原因,內(nèi)存只能按照頁面大?。ㄒ话?2位機(jī)為4KB,64位機(jī)為8KB)進(jìn)行分配,這樣就導(dǎo)致了當(dāng)我們僅需要幾個(gè)字節(jié)內(nèi)存時(shí),系統(tǒng)仍會返回一個(gè)頁面的內(nèi)存,顯然這是極度浪費(fèi)的。所以,不同于malloc的是,kmalloc的處理方式是:內(nèi)核先為其分配一系列不同大小(32B、64B、128B、… 、128KB)的內(nèi)存池,當(dāng)需要分配內(nèi)存時(shí),系統(tǒng)會分配大于等于所需內(nèi)存的最小一個(gè)內(nèi)存池給它。即kmalloc分配的內(nèi)存,最小為32字節(jié),最大為128KB。如果超過128KB,需要采樣其它內(nèi)存分配函數(shù),例如vmalloc()。

  • flag:該參數(shù)用于控制函數(shù)的行為,最常用的是GFP_KERNEL,表示當(dāng)當(dāng)前沒有足夠內(nèi)存分配時(shí),進(jìn)程進(jìn)入睡眠,待系統(tǒng)將緩沖區(qū)中的內(nèi)容SWAP到硬盤中后,獲得足夠內(nèi)存后再喚醒進(jìn)程,為其分配。更多標(biāo)志見下圖:
    linux內(nèi)存管理相關(guān)的函數(shù)有哪些

  • 使用 GFP_ KERNEL 標(biāo)志申請內(nèi)存時(shí),若暫時(shí)不能滿足,則進(jìn)程會睡眠等待頁,即會引起阻塞,因此不能在中斷上下文或持有自旋鎖的時(shí)候使用GFP_KERNE 申請內(nèi)存。所以,在中斷處理函數(shù)、tasklet 和內(nèi)核定時(shí)器等非進(jìn)程上下文中不能阻塞,此時(shí)驅(qū)動應(yīng)當(dāng)使用 GFP_ATOMIC 標(biāo)志來申請內(nèi)存。當(dāng)使用 GFP_ATOMIC 標(biāo)志申請內(nèi)存時(shí),若不存在空閑頁,則不等待,直接返回。

  • 除了上述表格所列標(biāo)志外,還包括如下

  • _ _GFP_DMA(要求分配在能夠 DMA 的內(nèi)存區(qū))

  • _ _GFP_HIGHMEM(指示分配的內(nèi)存可以位于高端內(nèi)存)

  • _ _GFP_COLD(請求一個(gè)較長時(shí)間不訪問的頁)

  • _ _GFP_NOWARN(當(dāng)一個(gè)分配無法滿足時(shí),阻止內(nèi)核發(fā)出警告)

  • _ _GFP_HIGH(高優(yōu)先級請求,允許獲得被內(nèi)核保留給緊急狀況使用的最后的內(nèi)存頁)

  • _ _GFP_REPEAT(分配失敗則盡力重復(fù)嘗試)

  • _ _GFP_NOFAIL(標(biāo)志只許申請成功,不推薦)

  • _ _GFP_NORETRY(若申請不到,則立即放棄)

  • 使用 kmalloc()申請的內(nèi)存應(yīng)使用 kfree()釋放,這個(gè)函數(shù)的用法和用戶空間的 free()類似。

2、vmalloc()

vmalloc()一般用在為只存在于軟件中(沒有對應(yīng)的硬件意義)的較大的順序緩沖區(qū)分配內(nèi)存,當(dāng)內(nèi)存沒有足夠大的連續(xù)物理空間可以分配時(shí),可以用該函數(shù)來分配虛擬地址連續(xù)但物理地址不連續(xù)的內(nèi)存。由于需要建立新的頁表,所以它的開銷要遠(yuǎn)遠(yuǎn)大于kmalloc及后面將要講到的__get_free_pages()函數(shù)。且vmalloc()不能用在原子上下文中,因?yàn)樗膬?nèi)部實(shí)現(xiàn)使用了標(biāo)志為 GFP_KERNEL 的kmalloc()。其函數(shù)原型如下:

void *vmalloc(unsigned long size);
void vfree(const void *addr);
  • 使用 vmalloc 函數(shù)的一個(gè)例子函數(shù)是 create_module()系統(tǒng)調(diào)用,它利用 vmalloc()函數(shù)來獲取被創(chuàng)建模塊需要的內(nèi)存空間。

  • 內(nèi)存分配是一項(xiàng)要求嚴(yán)格的任務(wù),無論什么時(shí)候,都應(yīng)該對返回值進(jìn)行檢測。

  • 在驅(qū)動編程中可以使用copy_from_user()對內(nèi)存進(jìn)行使用。下面舉一個(gè)使用vmalloc函數(shù)的示例:

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;
}

3、頁分配函數(shù)

在linux中,內(nèi)存分配是以頁為單位的,32位機(jī)中一頁為4KB,64位機(jī)中,一頁為8KB,但具體還有根據(jù)平臺而定。
根據(jù)返回值類型的不同,頁分配函數(shù)分為兩類,一是返回物理頁地址,二是返回虛擬地址。虛擬地址和物理地址起始相差一個(gè)固定的偏移量。

#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);
}

linux內(nèi)存管理相關(guān)的函數(shù)有哪些

根據(jù)返回頁面數(shù)目分類,分為僅返回單頁面的函數(shù)和返回多頁面的函數(shù)。

3.1 alloc_page()和alloc_pages()函數(shù)

該函數(shù)定義在頭文件/include/linux/gfp.h中,它既可以在內(nèi)核空間分配,也可以在用戶空間分配,它返回分配的第一個(gè)頁的描述符而非首地址,其原型為:

#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)  //分配連續(xù)2^order個(gè)頁面
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));
}

3.2 __get_free_pages()系列函數(shù)

它是kmalloc函數(shù)實(shí)現(xiàn)的基礎(chǔ),返回一個(gè)或多個(gè)頁面的虛擬地址。該系列函數(shù)/宏包括 get_zeroed_page()_ _get_free_page()_ _get_free_pages()。在使用時(shí),其申請標(biāo)志的值及含義與 kmalloc()完全一樣,最常用的是 GFP_KERNEL 和 GFP_ATOMIC。

/*分配多個(gè)頁并返回分配內(nèi)存的首地址,分配的頁數(shù)為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)

/*該函數(shù)返回一個(gè)指向新頁的指針并且將該頁清零*/
unsigned long get_zeroed_page(unsigned int flags);
  • 使用_ _get_free_pages()系列函數(shù)/宏申請的內(nèi)存應(yīng)使用free_page(addr)free_pages(addr, order)函數(shù)釋放:

#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()函數(shù)是調(diào)用__free_pages()函數(shù)完成內(nèi)存釋放的。

4、slab緩存

  • 當(dāng)在驅(qū)動程序中,遇到反復(fù)分配、釋放同一大小的內(nèi)存塊時(shí)(例如,inode、task_struct等),建議使用內(nèi)存池技術(shù)(對象在前后兩次被使用時(shí)均分配在同一塊內(nèi)存或同一類內(nèi)存空間,且保留了基本的數(shù)據(jù)結(jié)構(gòu),這大大提高了效率)。在linux中,有一個(gè)叫做slab分配器的內(nèi)存池管理技術(shù),內(nèi)存池使用的內(nèi)存區(qū)叫做后備高速緩存。

  • salb相關(guān)頭文件在linux/slab.h中,在使用后備高速緩存前,需要創(chuàng)建一個(gè)kmem_cache的結(jié)構(gòu)體。

4.1 創(chuàng)建slab緩存區(qū)

該函數(shù)創(chuàng)建一個(gè)slab緩存(后備高速緩沖區(qū)),它是一個(gè)可以駐留任意數(shù)目全部同樣大小的后備緩存。其原型如下:

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:創(chuàng)建的緩存名;
size:可容納的緩存塊個(gè)數(shù);
align:后備高速緩沖區(qū)中第一個(gè)內(nèi)存塊的偏移量(一般置為0);
flags:控制如何進(jìn)行分配的位掩碼,包括 SLAB_NO_REAP(即使內(nèi)存緊缺也不自動收縮這塊緩存)、SLAB_HWCACHE_ALIGN ( 每 個(gè) 數(shù) 據(jù) 對 象 被 對 齊 到 一 個(gè) 緩 存 行 )、SLAB_CACHE_DMA(要求數(shù)據(jù)對象在 DMA 內(nèi)存區(qū)分配)等);
ctor:是可選的內(nèi)存塊對象構(gòu)造函數(shù)(初始化函數(shù));
dtor:是可選的內(nèi)存對象塊析構(gòu)函數(shù)(釋放函數(shù))。

4.2 分配slab緩存函數(shù)

一旦創(chuàng)建完后備高速緩沖區(qū)后,就可以調(diào)用kmem_cache_alloc()在緩存區(qū)分配一個(gè)內(nèi)存塊對象了,其原型如下:

void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);

cachep指向開始分配的后備高速緩存,flags與傳給kmalloc函數(shù)的參數(shù)相同,一般為GFP_KERNEL。

4.3 釋放slab緩存

該函數(shù)釋放一個(gè)內(nèi)存塊對象:

void *kmem_cache_free(struct kmem_cache *cachep, void *objp);

4.4 銷毀slab緩存

kmem_cache_create對應(yīng)的是銷毀函數(shù),釋放一個(gè)后備高速緩存:

int kmem_cache_destroy(struct kmem_cache *cachep);

它必須等待所有已經(jīng)分配的內(nèi)存塊對象被釋放后才能釋放后備高速緩存區(qū)。

4.5 slab緩存使用舉例

創(chuàng)建一個(gè)存放線程結(jié)構(gòu)體(struct thread_info)的后備高速緩存,因?yàn)樵趌inux中涉及頻繁的線程創(chuàng)建與釋放,如果使用__get_free_page()函數(shù)會造成內(nèi)存的大量浪費(fèi),效率也不高。所以在linux內(nèi)核的初始化階段就創(chuàng)建了一個(gè)名為thread_info的后備高速緩存,代碼如下:

/* 創(chuàng)建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);

5、內(nèi)存池

在 Linux 內(nèi)核中還包含對內(nèi)存池的支持,內(nèi)存池技術(shù)也是一種非常經(jīng)典的用于分配大量小對象的后備緩存技術(shù)。

5.1 創(chuàng)建內(nèi)存池

mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, \
 							mempool_free_t *free_fn, void *pool_data);

mempool_create()函數(shù)用于創(chuàng)建一個(gè)內(nèi)存池,min_nr 參數(shù)是需要預(yù)分配對象的數(shù)目,alloc_fn 和 free_fn 是指向內(nèi)存池機(jī)制提供的標(biāo)準(zhǔn)對象分配和回收函數(shù)的指針,其原型分別為:

typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); 

typedef void (mempool_free_t)(void *element, void *pool_data);

pool_data 是分配和回收函數(shù)用到的指針,gfp_mask 是分配標(biāo)記。只有當(dāng)_ _GFP_WAIT 標(biāo)記被指定時(shí),分配函數(shù)才會休眠。

5.2 分配和回收對象

在內(nèi)存池中分配和回收對象需由以下函數(shù)來完成:

void *mempool_alloc(mempool_t *pool, int gfp_mask); 
void mempool_free(void *element, mempool_t *pool);

mempool_alloc()用來分配對象,如果內(nèi)存池分配器無法提供內(nèi)存,那么就可以用預(yù)分配的池。

5.3 銷毀內(nèi)存池

void mempool_destroy(mempool_t *pool);

mempool_create()函數(shù)創(chuàng)建的內(nèi)存池需由 mempool_destroy()來回收。

關(guān)于“l(fā)inux內(nèi)存管理相關(guān)的函數(shù)有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點(diǎn)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI