linux 最大值函数,Linux内核内存分配函数

实际的物理内存仅当进程真实访问新获取的虚拟地址时,才会由“请页机制”产生“缺页异常”,从而进入分配内存的程序“缺页异常”是虚拟内存机制的基本保证-----由它告诉内核为进程分配物理页,并建立页表。这样,虚拟地址才真实地映射了物理地址对于申请的内存必须释放,否则将寻致系统错误。

在内核中主要有以下几种内核分配函数,其主要区别和使用:

|--用户空间(动态分配)

--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告知分配器如果无法满足请求,立即返回。

内核内存的各个函数的异同如下表:

69b770d5002ce855ac8aca302d96aa20.png

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");

编译,在开发板上运行,可以得到如下结果:

3a872400f215acf0619fa22b3280ea65.png

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");

该范例编译后,开发板上运行效果如图:

f70e2b3e3d34f33e805bcacfbd6a5077.png

特别申明:

在内核中特别注意,对于申请的内存使用完毕必须释放,否则将导致系统错误。

在内核中常使用的内存申请大致代码范例如下,实际中依据需要修改。

范例:

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

}

编译后运行效果如下:

92754f30b40de1bcf7b3e433c719a8e1.png

此外内核还提供了一些更复杂的内存分配技术,包括后备缓冲区(look aside buffer)、slab和mempool等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值