0


Linux内核内存分配函数kmalloc、kzalloc和vmalloc

在内核环境中,常用的内存分配函数主要有kmalloc、kzalloc和vmalloc这三个。既然这三函数都能在内核申请空间,那么这三个函数有什么区别呢?如何选用呢?

kmalloc

首先是kmalloc,其函数原型为

// /include/linux/slab.hvoid*kmalloc(size_t size,gfp_t flags)

函数的特点:
申请的内存虚拟地址和物理地址都是连续的,允许申请的内存大小较小,具体的数值限制由平台和配置决定,具体可见kmalloc允许申请的内存大小。

常用的内存分配方法flags:

  • GFP_ATOMIC --表明分配内存的过程是原子的,不会被高优先级的进程或者中断打断
  • GFP_KERNEL – 常规分配
  • GFP_DMA – 给DMA分配内存时使用,使用该标志时分配的虚拟地址和物理地址都是连续的

常见的flags组合

  • 普通进程中,分配内存时可以睡眠: GFP_KERNEL
  • 普通进程中,分配内存时不可以睡眠: GFP_ATOMIC
  • 中断处理程序,tasklet:GFP_ATOMIC
  • DMA,可以睡眠:GFP_DMA | GFP_KERNEL
  • DMA,不可以睡眠:GFP_DMA | GFP_ATOMIC

kzalloc

与kmalloc基本一致,就是kmalloc的特例,是后者的封装,在flags与上了__GFP_ZERO这个标志,表示在分配完内存后对分配的内存进行清零,所以使用起来比较方便。

// /include/linux/slab.hstaticinline__alloc_size(1)void*kzalloc(size_t size,gfp_t flags){returnkmalloc(size, flags | __GFP_ZERO);}

kzalloc和kmalloc所对应的内存释放函数都是kfree

voidkfree(constvoid*objp);

vmalloc

函数原型

// /include/linux/vmalloc.hvoid*vmalloc(unsignedlong size);

vmalloc申请的内存的虚拟地址是连续的,但其对应的物理地址不是连续的,因此其申请的内存大小没有限制,因此申请较大的内存时可以使用这个函数。vmalloc申请内存的过程中可以睡眠,因此不能用于中断上下文中。

对应的内存释放函数为

voidvfree(constvoid*addr);

一般来说,为了性能,通常使用kzalloc/kmalloc分配内存,如果要分配的内存过大则使用vmalloc

示例程序

//mem_alloc.c#include<linux/slab.h>#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("GPL");staticint __init mem_alloc_init(void);staticvoid __exit mem_alloc_exit(void);#defineKMALLOC_SIZE131072#defineKZALLOC_SIZE131072#defineVMALLOC_SIZE4194304//4096 * 1024 byteschar* kmalloc_pt =NULL;char* kmalloc_pt_large =NULL;char* kzalloc_pt =NULL;char* vmalloc_pt =NULL;int __init mem_alloc_init(void){printk("####### Enter Module #######\n");
    kmalloc_pt =(char*)kmalloc(KMALLOC_SIZE, GFP_KERNEL);if(kmalloc_pt ==NULL){return-1;}else{printk("kmalloc 128 KB memory successfully!, addr=0x%lx\n",(unsignedlong)kmalloc_pt);}//用kmalloc分配4096kb内存,会失败
    kmalloc_pt_large =(char*)kmalloc(VMALLOC_SIZE, GFP_KERNEL);if(kmalloc_pt_large ==NULL){printk("kmalloc 4096 KB memory failed.\n");}else{printk("kmalloc 4096 KB memory successfully!, addr=0x%lx\n",(unsignedlong)kmalloc_pt_large);}
    kzalloc_pt =(char*)kzalloc(KZALLOC_SIZE, GFP_KERNEL);if(kzalloc_pt ==NULL){return-1;}else{printk("kzalloc 128 KB memory successfully!, addr=0x%lx\n",(unsignedlong)kzalloc_pt);}//用vmalloc分配4096kb内存,成功
    vmalloc_pt =(char*)vmalloc(VMALLOC_SIZE);if(vmalloc_pt ==NULL){return-1;}else{printk("vmalloc 4096 KB memory successfully!, addr=0x%lx\n",(unsignedlong)vmalloc_pt);}printk("#####################\n");return0;}void __exit mem_alloc_exit(void){printk("####### Exit Module #######\n");if(kmalloc_pt){kfree(kmalloc_pt);printk("kfree kmalloc_pt\n");
        kmalloc_pt =NULL;}if(kmalloc_pt_large){kfree(kmalloc_pt_large);printk("kfree kmalloc_pt_large\n");
        kmalloc_pt_large =NULL;}if(kzalloc_pt){kfree(kzalloc_pt);printk("kfree kzalloc_pt\n");
        kzalloc_pt =NULL;}if(vmalloc_pt){vfree(vmalloc_pt);printk("vfree vmalloc_pt\n");
        vmalloc_pt =NULL;}printk("exit module.\n");printk("#####################\n");}module_init(mem_alloc_init);module_exit(mem_alloc_exit);

编译完后使用insmod命令加载模块,不使用该模块时使用rmmod命令卸载模块
加载和卸载模块
使用dmesg命令查看模块加载和卸载的输出
加载输出
加载卸载输出
从输出中可以看到,在我的电脑上kmallockzalloc可以成功分配128kb内存,但在分配更大的4096kb内存时则失败了,而vmalloc则可以成功分配4096kb的内存。

关于kmalloc/kzalloc具体最大可以分配多大的内存由平台和配置决定,不是一个固定的值,想要弄清楚需要阅读源码。

由上,在内核编程时,申请较小内存时应该优先使用kmalloc/kzalloc,在申请较大内存时使用vmalloc

参考

https://www.coolcou.com/linux-kernel/linux-kernel-memory-management-api/the-linux-kernel-kzalloc.html
https://blog.csdn.net/lu_embedded/article/details/51588902

https://blog.csdn.net/lunhui2016/article/details/114297346

标签: linux 服务器 c语言

本文转载自: https://blog.csdn.net/weixin_40313940/article/details/128563729
版权归原作者 静待梅花开 所有, 如有侵权,请联系我们删除。

“Linux内核内存分配函数kmalloc、kzalloc和vmalloc”的评论:

还没有评论