搜索
首页运维linux运维linux内存管理相关的几个函数

linux内存管理相关的函数:1、kmalloc(),用于内核态的内存分配;2、vmalloc(),一般用在为只存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存;3、alloc_page()和alloc_pages()函数,可以在内核空间分配;4、__get_free_pages()系列函数,返回一个或多个页面的虚拟地址;5、kmem_cache_alloc()等。

linux内存管理相关的几个函数

本教程操作环境:linux7.3系统、Dell G3电脑。

本文叙述了在Linux内核中常见的几种内存分配函数及其异同,对理解linux底层内存分配机制有个较好理解。

1、kmalloc()

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()类似。

2、vmalloc()

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

3、页分配函数

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

在这里插入图片描述

根据返回页面数目分类,分为仅返回单页面的函数和返回多页面的函数。

3.1 alloc_page()和alloc_pages()函数

该函数定义在头文件/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));
}

3.2 __get_free_pages()系列函数

它是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()函数完成内存释放的。

4、slab缓存

  • 当在驱动程序中,遇到反复分配、释放同一大小的内存块时(例如,inode、task_struct等),建议使用内存池技术(对象在前后两次被使用时均分配在同一块内存或同一类内存空间,且保留了基本的数据结构,这大大提高了效率)。在linux中,有一个叫做slab分配器的内存池管理技术,内存池使用的内存区叫做后备高速缓存。
  • salb相关头文件在linux/slab.h中,在使用后备高速缓存前,需要创建一个kmem_cache的结构体。

4.1 创建slab缓存区

该函数创建一个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:是可选的内存对象块析构函数(释放函数)。

4.2 分配slab缓存函数

一旦创建完后备高速缓冲区后,就可以调用kmem_cache_alloc()在缓存区分配一个内存块对象了,其原型如下:

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

cachep指向开始分配的后备高速缓存,flags与传给kmalloc函数的参数相同,一般为GFP_KERNEL。

4.3 释放slab缓存

该函数释放一个内存块对象:

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

4.4 销毁slab缓存

kmem_cache_create对应的是销毁函数,释放一个后备高速缓存:

int kmem_cache_destroy(struct kmem_cache *cachep);

它必须等待所有已经分配的内存块对象被释放后才能释放后备高速缓存区。

4.5 slab缓存使用举例

创建一个存放线程结构体(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);

5、内存池

在 Linux 内核中还包含对内存池的支持,内存池技术也是一种非常经典的用于分配大量小对象的后备缓存技术。

5.1 创建内存池

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 标记被指定时,分配函数才会休眠。

5.2 分配和回收对象

在内存池中分配和回收对象需由以下函数来完成:

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

mempool_alloc()用来分配对象,如果内存池分配器无法提供内存,那么就可以用预分配的池。

5.3 销毁内存池

void mempool_destroy(mempool_t *pool);

mempool_create()函数创建的内存池需由 mempool_destroy()来回收。

相关推荐:《Linux视频教程

以上是linux内存管理相关的几个函数的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Linux的5支支柱:了解他们的角色Linux的5支支柱:了解他们的角色Apr 11, 2025 am 12:07 AM

Linux系统的五大支柱是:1.内核,2.系统库,3.Shell,4.文件系统,5.系统工具。内核管理硬件资源并提供基本服务;系统库为应用程序提供预编译函数;Shell是用户与系统交互的接口;文件系统组织和存储数据;系统工具用于系统管理和维护。

Linux维护模式:工具和技术Linux维护模式:工具和技术Apr 10, 2025 am 09:42 AM

在Linux系统中,可以通过在启动时按特定键或使用命令如“sudosystemctlrescue”进入维护模式。维护模式允许管理员在不受干扰的情况下进行系统维护和故障排除,如修复文件系统、重置密码、修补安全漏洞等。

关键Linux操作:初学者指南关键Linux操作:初学者指南Apr 09, 2025 pm 04:09 PM

Linux初学者应掌握文件管理、用户管理和网络配置等基本操作。1)文件管理:使用mkdir、touch、ls、rm、mv、cp命令。2)用户管理:使用useradd、passwd、userdel、usermod命令。3)网络配置:使用ifconfig、echo、ufw命令。这些操作是Linux系统管理的基础,熟练掌握它们可以有效管理系统。

如何使用sudo向Linux的用户授予高架特权?如何使用sudo向Linux的用户授予高架特权?Mar 17, 2025 pm 05:32 PM

本文解释了如何管理Linux中的Sudo特权,包括授予,撤销和安全性最佳实践。关键重点是安全和sudoers安全和限制访问。Character数量:159

如何在Linux中实现SSH的两因素身份验证(2FA)?如何在Linux中实现SSH的两因素身份验证(2FA)?Mar 17, 2025 pm 05:31 PM

本文提供了有关使用Google Authenticator在Linux上设置两因素身份验证(2FA)的指南,详细介绍了安装,配置和故障排除步骤。它突出了2FA的安全益处,例如增强的SEC

如何使用TOP,HTOP和VMSTAT等工具来监视Linux中的系统性能?如何使用TOP,HTOP和VMSTAT等工具来监视Linux中的系统性能?Mar 17, 2025 pm 05:28 PM

本文讨论了使用TOP,HTOP和VMSTAT监视Linux系统性能,并详细介绍其独特功能和自定义选项,以进行有效的系统管理。

如何使用软件包管理器(APT,YUM,DNF)管理Linux中的软件包?如何使用软件包管理器(APT,YUM,DNF)管理Linux中的软件包?Mar 17, 2025 pm 05:26 PM

文章讨论了使用APT,YUM和DNF在Linux中管理软件包,涵盖安装,更新和删除。它比较了它们对不同分布的功能和适用性。

如何在Linux中使用正则表达式(REGEX)进行模式匹配?如何在Linux中使用正则表达式(REGEX)进行模式匹配?Mar 17, 2025 pm 05:25 PM

本文介绍了如何在Linux中使用正则表达式(REGEX)进行模式匹配,文件搜索和文本操作,详细列式,命令和工具,例如GREP,SED和AWK。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用