本人无才,非专业出生,笔记仅记录学习,其中很多基于网络或他人原创,如有侵权和冒犯,或者错误,请谅解指正……谢谢!
分类: LINUX
2015-05-21 15:38:15
在内核中主要有以下几种内核分配函数,其主要区别和使用:
|--用户空间(动态分配)
--malloc /free 按字节分配内存
--new/delete
--vmalloc/free 分配的内存按页对齐
|--内核空间
-- kmalloc/kfree: 分配的内存物理上连续,只能在低端内存分配;
-- vmalloc/vfree:分配的内存在内核空间中连续,物理上无需连续;
-- get_zeroed_page/free_page: 分配一个页面,并且该页面内容被清零,只能在低端内存分配;
-- __get_free_pages/free_pages: 分配指定页数的低端内存,只能从高端内存分配;
--alloc_pages/__free_pages: 分配指定页数的内存,可以从高端内存,也可以从低端内存分配。
内核管理系统的物理内存,物理内存只能按页面进行分配。kmalloc 和典型的用户空间 malloc 在实际上有很大的差别,内核使用特殊的基于页的分配技术,以最佳的方式利用系统 RAM。Linux 处理内存分配的方法:创建一系列内存对象集合,每个集合内的内存块大小是固定。处理分配请求时,就直接在包含有足够大内存块的集合中传递一个整块给请求者。
必须注意的是:内核只能分配一些预定义的、固定大小的字节数组。kmalloc 能够处理的最小内存块是 32 或 64 字节(体系结构依赖),而内存块大小的上限随着体系和内核配置而变化。考虑到移植性,不应分配大于 128 KB的内存。若需多于几个 KB的内存块,最好使用其他方法。
内存分配最终总是调用 __get_free_pages 来进行实际的分配,这就是 GFP_ 前缀的由来。所有标志都定义在
内核内存分配标志:
分配优先级的标志:
–GFP_KERNEL: 表示该次内存分配由内核态进程调用,返是内核内存的正常分配,如果空闲空间不够, 该次分配将使得进程进入睡眠,等待空闲页出现。
最常用的标志,意思是这个分配代表运行在内核空间的进程进行。内核正常分配内存。当空闲内存较少时,可能进入休眠来等待一个页面。当前进程休眠时,内核会采取适当的动作来获取空闲页。所以使用 GFP_KERNEL 来分配内存的函数必须是可重入,且不能在原子上下文中运行。
–GFP_ATOMIC: 用于分配请求不是来自于进程上下文,而是来自于中断、任务队列处理、内核定时器等中 断上下文的情况,此时不能进入睡眠。
内核通常会为原子性的分配预留一些空闲页。当当前进程不能被置为睡眠时,应使用 GFP_ATOMIC,这样kmalloc 甚至能够使用最后一个空闲页。如果连这最后一个空闲页也不存在,则分配返回失败。常用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。
- GFP_USER:用来为用户空间分配内存页,可能睡眠。
- GFP_HIGHUSER:类似GFP_USER,如果有高端内存,就从高端内存分配。
- GFP_NOIO/GFP_NOFS:功能类似 GFP_KERNEL,但是为内核分配内存的工作增加了限制。具有 GFP_NOFS 的分配不允许执行任何文件系统调用,而GFP_NOIO 禁止任何 I/O 初始化。它们主要用 在文件系统和虚拟内存代码。那里允许分配休眠,但不应发生递归的文件系统调。
控制分配方式的标志:(有的标志用双下划线做前缀,他们可与上面标志“或”起来使用)
–__GFP_DMA: 用于分配用于DMA(Direct memoryaccess-直接内存访问)功能的内存区(通常物理 地址在16M以下)。
–__GFP_HIGHMEM: 用于分配的内存可以位于高端内存的情况(通常在物理内存896M以上)
–__GFP_COLD:通常,分配器试图返回“缓存热(cache warm)”页面(可在处理器缓存中找到的页面)。 而这个标志请求一个尚未使用的“冷”页面。对于用作 DMA 读取的页面分配,可使用此标志。因为此 时页面在处理器缓存中没多大帮助。
–__GFP_NOWARN:当一个分配无法满足,阻止内核发出警告(使用 printk )。
–__GFP_HIGH:高优先级请求,允许为紧急状况消耗被内核保留的最后一些内存页。
–__GFP_REPEAT/__GFP_NOFAIL/__GFP_NORETRY:告诉分配器当满足一个分配有困难时,如何动作。 __GFP_REPEAT 表示努力再尝试一次,仍然可能失败;
__GFP_NOFAIL 告诉分配器尽最大努力 来满足要求,始终不返回失败,不推荐使用;
__GFP_NORETRY 告知分配器如果无法满足请求,立即返回。
内核内存的各个函数的异同如下表:
A、kmalloc/kfree函数:
该函数分配的内存物理上连续,从ZONE_NORMAL区域返回连续内存的内存分配函数,只能在低端内存分配。
函数原型如下:
void *kmalloc(int count, int flags);
count是要分配的字节数,flags是一个模式说明符。支持的所有标志列在include/linux./gfp.h文件中(gfp是get free page的缩写),如下为常用标志。
(1) GFP_KERNEL,被进程上下文用来分配内存。如果指定了该标志,kmalloc()将被允许睡眠,以等待其他页被释放。
(2) GFP_ATOMIC,被中断上下文用来获取内存。在这种模式下,kmalloc()不允许进行睡眠等待,以获得空闲页,因此GFP_ATOMIC分配成功的可能性比用GFP_KERNEL低。
使用kmalloc申请的内存需要kfree来释放,其函数原型如下:
void kfree(const void *objp);
其中参数objp: 由kmalloc返回的内核虚拟地址。
由于kmalloc()返回的内存保留了以前的内容,将它暴露给用户空间可到会导致安全问题,因此我们可以使用kzalloc()获得被填充为0的内存。
B、get_zeroed_page/free_page函数:
该函数分配一个页面,并且该页面内容被清零,只能在低端内存分配。函数原型如下:
unsigned long get_zeroed_page(gfp_t gfp_mask);
参数说明:gfp_mask: 分配标志,用于控制kmalloc行为。
返回值: 指向分配到的已经被清零的内存页面第一个字节的指针,失败返回NULL。
void free_pages(unsigned long addr);
参数addr: 由get_zeroed_page返回的内核虚拟地址。
下面通过一个实例来看看内核空间的动态内存申请方式,使用kmalloc和get_zeroed_page函数:
//Day01/KernelspaceDemo02.c /*This demo is for how to use kmalloc and get_zeroed_page function in the kernel space*/
#include
#include
#include
#include
static char *buf_k=NULL;/*the data for kmalloc function*/ static char *buf_g=NULL;/*the data for get_zeroed_page function*/
/*initialize module function */ static int __init kernelspacedemo02_init(void){ printk(KERN_ALERT "Entry kernelspacedemo02_init...\n");
/*allocate memory from kernel space by kmalloc */ buf_k=(char *)kmalloc(100,GFP_KERNEL); if(IS_ERR(buf_k)){ printk(KERN_ALERT "Failure to allocate memory from kernel 100 Bytes ...\n"); goto failure_alloc_buf_k; }
memset(buf_k,0,100); strcpy(buf_k, "Congratulate ! Success to allocate memory from kernel 100 Bytes ...\n"); printk(KERN_ALERT "Buffure of buf_k:%s\n",buf_k);
/*allocate memory from kernel space by get_zeroed_page */ buf_g=(char *)get_zeroed_page(GFP_KERNEL); if(IS_ERR(buf_g)){ printk(KERN_ALERT "Failure to allocate one page memory from kernel...\n"); goto failure_alloc_buf_g; }
strcpy(buf_g, "Congratulate ! Success to allocate one page memory from kernel...\n"); printk(KERN_ALERT "Buffure of buf_g:%s\n",buf_g);
return 0;
failure_alloc_buf_g: kfree(buf_k); buf_k=NULL; failure_alloc_buf_k: return -ENOMEM;
}
/*clean up module function */ static void __exit kernelspacedemo02_exit(void){ printk(KERN_ALERT "Entry kernelspacedemo02_exit...\n");
/*free the memory allocate by kmalloc and get_zeroed_page function */ free_page((unsigned long )buf_g); buf_g=NULL; kfree(buf_k); buf_k=NULL; } /*The declaration of initialization and cleaning up module functions*/ module_init(kernelspacedemo02_init); module_exit(kernelspacedemo02_exit);
/*The information of this module*/ MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("JACK_ZHANG"); MODULE_DESCRIPTION("Use kmalloc and get_zeroed_page function to allocate memory in the kernel space"); MODULE_VERSION("0.0.1"); MODULE_ALIAS("KernelSpaceKmalloc"); |
编译,在开发板上运行,可以得到如下结果:
C、__get_free_pages/free_pages函数:
该函数分配指定页数的低端内存,只能从高端内存分配。函数原型如下:
unsigned long __get_free_pages(gfp_t gfp_mask,unsigned int order);
参数说明:
gfp_mask:分配标志,用于控制__get_free_pages行为
order: 请求或释放的页数的2的幂,例如:操作1页该值为0,操作16页该值为4。
如果该值太大会寻致分配失败,该值允许的最大值依赖于体系结构。
返回: 指向分配到的连续内存区第一个字节的指针,失败返回NULL。
free_pages的函数原型如下:
void free_pages(unsigned long addr, unsigned intorder);
参数说明:
addr: 由__get_free_pages返回的内核虚拟地址
order: __get_free_pages分配内存时使用的值
int get_order(unsigned long size);
size: 需要计算order值的大小(按字节计算);
返回: __ get_free_pages等函数需要的order值。
D、alloc_pages宏/__free_pages函数:
该宏可以分配指定页数的内存,可以从高端内存,也可以从低端内存分配。
函数如下:
struct page *alloc_pages_node(gfp_t gfp_mask,unsigned int order);
gfp_mask:分配标志,用于控制__get_free_pages行为。
order: 请求或释放的页数的2的幂,例如:操作1页该值为0,操作16页该值为4。
如果该值太大会寻致分配失败,该值允许的最大值依赖于体系结构
返回: 指向分配到的物理页面中的第一个页的structpage指针,失败返回NULL。
E、vmalloc/vfree函数:
分配的内存在内核空间中连续,物理上无需连续。vmalloc由于不需要物理上也连续,所以性能很差。 一般只有在必须申请大块内存时才使用,例如动态插入模块时。
如果需要分配大的内存缓冲区,而且也不要求内存在物理上有联系,可以用vmalloc()代替kmalloc():
void *vmalloc(unsigned long size);
size: 待分配的内存大小,自动按页对齐
返回: 分配到的内核虚拟地址,失败返回NULL
vmalloc()需要比kmalloc()更大的分配空间,但是它更慢,而且不能从中断上下文调用。另外,不能用vmalloc()返回的物理上不连 续的内存执行DMA。
在设备打开时,高性能的网络驱动程序通常会使用vmalloc()来分配较大的描述符环行缓冲区。
Vfree函数原型:
void vfree(const void *addr);
addr: 由vmalloc返回的内核虚拟地址。
下面的示例在内核模块中使用了kmalloc,__get_free_pages,以及vmalloc函数,请注意区分其在实际使用的区别:
//Day01/KernelspaceDemo03.c /*This demo is for how to use kmalloc vmalloc and __get_free_pages function in the kernel address space*/
#include
#include
#include
#include
#include
static unsigned char *kernelkmalloc =NULL;/*the data for kmalloc function*/ static unsigned char *kernelpagemem=NULL;/*the data for __get_free_pages function*/ static unsigned char *kernelvmalloc=NULL;/*the data for vmalloc function*/ static int order =1;
/*initialize module function */ static int __init kernelspacedemo03_init(void){ int ret=-ENOMEM;
printk(KERN_ALERT "Entry kernelspacedemo03_init...\n");
/*allocate memory from kernel space by kmalloc function*/ kernelkmalloc=(unsigned char *)kmalloc(100,GFP_KERNEL); if(IS_ERR(kernelkmalloc)){ printk(KERN_ALERT "Failure to allocate memory use kmalloc from kernel ...\n"); goto failure_kernelkmalloc; }
printk("Congratulate ! Success to allocate memory use kmalloc from kernel ...\n"); printk(KERN_ALERT "The space address is : 0x%lx \n",(unsigned long )kernelkmalloc);
/*allocate memory from kernel space by __get_free_page function */ kernelpagemem=(unsigned char *) __get_free_pages(GFP_KERNEL,order); if(IS_ERR(kernelpagemem)){ printk(KERN_ALERT "Failure to get pages from kernel...\n"); goto failure_kernelpagemem; }
printk(KERN_ALERT"Congratulate ! Success to allocate free pages memory from kernel...\n"); printk(KERN_ALERT "And this pages address:0x%lx\n",(unsigned long)kernelpagemem);
/*allocate memory from kernel space by vmalloc function */ kernelvmalloc=(unsigned char *) vmalloc (1024*1024); if(IS_ERR(kernelvmalloc)){ printk(KERN_ALERT "Failure to get memory by vmalloc function from kernel...\n"); goto failure_kernelvmalloc; }
printk(KERN_ALERT"Congratulate ! Success to allocate memory from kernel...\n"); printk(KERN_ALERT "And this pages address:0x%lx\n",(unsigned long)kernelvmalloc);
return 0;
failure_kernelvmalloc: free_pages((unsigned long)kernelpagemem,order); kernelpagemem=NULL;
failure_kernelpagemem: kfree(kernelkmalloc); kernelkmalloc=NULL;
failure_kernelkmalloc: return ret;
}
/*clean up module function */ static void __exit kernelspacedemo03_exit(void){ printk(KERN_ALERT "Entry kernelspacedemo03_exit...\n");
/*free the memory allocate by kmalloc/ __get_free_pages/vmalloc function */ vfree(kernelvmalloc); kernelvmalloc=NULL; free_pages((unsigned long)kernelpagemem,order); kernelpagemem=NULL; kfree(kernelkmalloc); kernelkmalloc=NULL;
} /*The declaration of initialization and cleaning up module functions*/ module_init(kernelspacedemo03_init); module_exit(kernelspacedemo03_exit);
/*The information of this module*/ MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("JACK_ZHANG"); MODULE_DESCRIPTION("Use kmalloc/ get_free_pages/vmalloc function to allocate memory in the kernel space"); MODULE_VERSION("0.0.1"); MODULE_ALIAS("KernelAddressSpace"); |
该范例编译后,开发板上运行效果如图:
特别申明:
在内核中特别注意,对于申请的内存使用完毕必须释放,否则将导致系统错误。
在内核中常使用的内存申请大致代码范例如下,实际中依据需要修改。
范例:
//Day01/KernelspaceDemo01.c
#include
#include
#include
#include
#include #define PAGE_NUM 4
unsigned char * kernelkmalloc = NULL; unsigned char * kernelkpagemen = NULL; unsigned char * kernelvmalloc = NULL;
static int kernelspace_init (void){ /*错误编号*/ int ret= -ENOMEM;
/*kmalloc的第二个参数flags: * GFP_KERNEL:分配内存,分配过程中可能导致睡眠,没有分配到就睡眠等待 * GFP_ATOMIC:分配过程中不会导致睡眠,没有分配到就直接返回 * __GFP_DMA :申请到的内存通常位于0——16M区间 * __GFP_HIGHMEM :申请高端内存 (896M以上的物理地址) */ kernelkmalloc = (unsigned char *)kmalloc(100,GFP_KERNEL);
/*判断内存是否申请分配成功。如果成功该宏返回的是分配成功的地址,出错则回的错误编号*/ if(IS_ERR(kernelkmalloc)){ printk("Kmalloc failed !\n"); ret = PTR_ERR(kernelkmalloc); goto failure_kmalloc;/*使用goto语句做出错处理直接返回*/ } printk("kmalloc space :0X%lx \n",(unsigned long )kernelkmalloc);
/*__get_free_page的第二个参数order: * 请求或释放的页数的2的幂(比如是4,表示分配16页的空间) * 一般先定义宏,程序逻辑上容易理解 */ kernelkpagemen = (unsigned char *)__get_free_pages(GFP_KERNEL,PAGE_NUM);
/* 在内核空间程序,如果第一次申请成功、第二次失败,返回的时候须要释放第一次分配的内存 * 如果在应用空间,进程结束,所申请和打开的资源,系统会自动回收和关闭 * 这一点在内核编程中尤其重要,否则会发生内存泄漏等问题 */ if(IS_ERR(kernelkpagemen)){ printk("Kmalloc failed !\n"); ret = PTR_ERR(kernelkmalloc); goto failure_get_free_page ;/*使用goto语句做出错处理直接返回*/ } printk("get_free_page :0X%lx \n",(unsigned long )kernelkpagemen);
kernelvmalloc = (unsigned char *)vmalloc(1024*1024); if(IS_ERR(kernelvmalloc)){ printk("vmalloc failed !\n"); ret = PTR_ERR(kernelvmalloc); goto failure_vmalloc ;/*使用goto语句做出错处理直接返回*/ } printk("vmalloc address :0X%lx \n",(unsigned long )kernelvmalloc);
return 0;
/*出错处理*/ failure_vmalloc: free_pages((unsigned long)kernelkpagemen,PAGE_NUM); failure_get_free_page: kfree(kernelkmalloc);/*若执行到此处,也会执行下面的return ret*/ failure_kmalloc: return ret;
}
/*模块卸载的时候,需要释放所申请的内存*/ static void kernelspace_exit (void){ vfree(kernelvmalloc); free_pages((unsigned long)kernelkpagemen,PAGE_NUM); kfree(kernelkmalloc); }
MODULE_LICENSE ("Dual BSD/GPL"); module_init(kernelspace_init); module_exit(kernelspace_exit); |
而基于客户端的代码则如下:
/*This demo is for how to use malloc function in the userspace*/
#include
#include
#include
static char *dat =NULL; static long allcnt =50*1024*1024;
int main( int argc,char **argv){ char ch; printf("Starting demostration:\n"); printf("Press \'a\' key to allocate space...\n"); ch =getchar(); if('a'!=ch){ printf("You press error key !\n"); exit(-1); }
/*allocate memory*/ dat=malloc(allcnt); if(NULL==dat){ printf("Failure to allocate memory size:%ld...\n",allcnt); exit(-2); } printf("Success to allocate memory size:%ld...\n",allcnt);
/*Using memory allocate*/ printf("Press \'u\' key to use memory ...\n"); ch=getchar(); if('u'!= ch){ printf("You press error key !\n"); free(dat); exit(-3); } printf("Using memory allocated...\n"); memset(dat,0,allcnt);
/*Free memory and exit*/ printf("Press any key to exit...\n"); getchar(); free(dat); dat=NULL;
return 0; } |
编译后运行效果如下:
此外内核还提供了一些更复杂的内存分配技术,包括后备缓冲区(look aside buffer)、slab和mempool等。