本文主要介绍“linux内存管理相关的函数有哪些?”主机频道通过实际案例向你展示操作流程。操作方法简单、快捷、实用。希望这篇文章“linux内存管理相关的功能有哪些?”可以帮你解决问题。
与linux内存管理相关的函数:1 .kmalloc(),用于内核模式下的内存分配;2.vmalloc()一般用于为只存在于软件中的大型顺序缓冲区分配内存(没有相应的硬件意义);3.alloc_page()和alloc_pages()函数可以在内核空间分配;4.__get_free_pages()系列函数,返回一个或多个页面的虚拟地址;5.kmem_cache_alloc()等。
1.kmalloc()kmalloc()函数类似于我们常见的malloc()函数,前者用于内核态的内存分配,后者用于用户态。kmalloc()函数在物理内存中分配一个连续的存储空间,和malloc()函数一样,它不会清除其中的原始数据。如果内存足够,其分配速度非常快。其原型如下:
静态内联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,是指当目前没有足够的内存分配时,进程进入睡眠状态,系统将缓冲区中的内容交换到硬盘后,唤醒进程并进行分配。更多标志见下图:
使用GFP_ KERNEL标志申请内存时,如果暂时不能满足,进程会休眠等待页面,会造成阻塞,所以在中断上下文或者持有自旋锁的情况下,不能使用GFP _ kernel申请内存。所以不能在中断处理程序、小任务、内核定时器等非进程上下文中阻塞。这个时候驱动要用GFP_ATOMIC标志来申请内存。使用GFP_ATOMIC标志申请内存时,如果没有空闲页,不要等待,直接返回。
除了上表中列出的标志外,还包括以下标志:
_ _GFP_DMA(要求分配在支持DMA的存储区中)
_ _ GFP _ highmeme(表示分配的内存可以位于高端内存中)
_ _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(无符号长整型);
void vfree(const void * addr);使用vmalloc函数的一个例子是create_module()系统调用,它使用vmalloc()函数来获取所创建的模块所需的内存空间。
内存分配是一项要求很高的任务,应该随时测试返回值。
您可以使用copy_from_user()在驱动程序编程中使用内存。下面是一个使用vmalloc函数的示例:
静态int xxx(...)
{
...
cpuid _ entries = vmalloc(sizeof(struct KVM _ cpuid _ entry)* cpuid-& gt;东北);
如果(!cpuid_entries)
外出;
if(copy _ from _ user(cpuid _ entries,entries,cpuid-& gt;ent * sizeof(struct KVM _ cpuid _ entry)))
goto out _ free
for(I = 0;我& ltcpuid-& gt;东北;i++){
vcpuid-& gt;arch.cpuid_entries
根据返回的页面数量,分为只返回单个页面的函数和返回多个页面的函数。
3.1 alloc_page()和alloc_pages()函数这个函数是在头文件/include/Linux/gfp.h中定义的,既可以在内核空间分配,也可以在用户空间分配,它返回分配的第一页的描述符,而不是第一个地址。它的原型是:
#定义分配页面(gfp_mask)分配页面(gfp_mask,0)
# define alloc _ pages (GFP _ mask,order)alloc _ pages _ node(numa _ node _ id(),GFP _ mask,order)//分配2个连续的页面。
静态内嵌结构page *alloc_pages_node(int nid,gfp_t gfp_mask,unsigned int order)
{
如果(不太可能(order & gt= MAX_ORDER))
返回NULL
if(NID & lt;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页),视具体情况而定。
硬件平台基于。*/
无符号long _ _ get _ free _ pages(GFP _ t GFP _ mask,无符号int order)
{
struct page * page
page = alloc_pages(gfp_mask,order);
如果(!页面)
返回0;
return(无符号长整型)page _ address(page);
}
# define _ _ get _ free _ page(GFP _ mask)_ _ get _ free _ pages(GFP _ mask,0)
/*此函数返回一个指向新页面的指针,并清除该页面*/
无符号长整型get_zeroed_page(无符号整型标志);使用_ _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(无符号long addr,无符号int order)
{
if(addr!= 0){
VM_BUG_ON(!virt _ addr _ valid((void *)addr));
_ _ free _ pages(virt _ to _ page((void *)addr),order);
}
}
void __free_pages(结构页*页,无符号整数顺序)
{
if(put_page_testzero(page)){
if(order == 0)
free_hot_page(页面);
其他
__free_pages_ok(page,order);
}
函数调用函数释放内存。
4.在驱动中重复分配和释放相同大小的内存块时的slab缓存(例如,inode、task_struct等。),建议使用内存池技术(对象前后两次使用时分配在同一个内存或同类型的内存空间中,保留基本的数据结构,大大提高了效率)。在linux中,有一种内存池管理技术叫做slab allocator,内存池使用的内存区域叫做备份缓存。
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,无符号长标志,\
void (*ctor)(void *,struct kmem_cache *,无符号长整型),\
void (*dtor)(void *,struct kmem_cache *,unsigned ONG));其中:name:创建的缓存的名称;大小:可以容纳的缓存块数;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标志);Cachep指向开始分配的备份缓存,flags与传递给kmalloc函数的参数相同,一般为GFP_KERNEL。
4.3释放平板缓存该函数释放一个内存块对象:
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 cache用一个例子来创建一个备份缓存,用于存储线程结构(struct thread_info)。由于linux中涉及到频繁的线程创建和释放,如果使用__get_free_page()函数,会造成大量内存浪费,效率不高。于是在linux内核初始化阶段,创建一个名为thread_info的备份缓存,代码如下:
/*创建片缓存*/
静态结构kmem _ cache * thread _ info _ cache
thread _ info _ cache = kmem _ cache _ create线程信息& quot,sizeof(结构线程信息),\
SLAB_HWCACHE_ALIGN|SLAB_PANIC,NULL,NULL);
/*分配片缓存*/
struct thread _ info * ti
ti = kmem _ cache _ alloc(thread _ info _ cache,GFP _ KERNEL);
/*使用平板缓存*/
...
/*释放平板缓存*/
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()回收。
评论前必须登录!
注册