在内核环境中,常用的内存分配函数主要有kmalloc、kzalloc和vmalloc这三个。既然这三函数都能在内核申请空间,那么这三个函数有什么区别呢?如何选用呢?
kmalloc
首先是kmalloc,其函数原型为
// /include/linux/slab.h
void *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.h
static inline __alloc_size(1) void *kzalloc(size_t size, gfp_t flags)
{
return kmalloc(size, flags | __GFP_ZERO);
}
kzalloc和kmalloc所对应的内存释放函数都是kfree
void kfree(const void *objp);
vmalloc
函数原型
// /include/linux/vmalloc.h
void *vmalloc(unsigned long size);
vmalloc申请的内存的虚拟地址是连续的,但其对应的物理地址不是连续的,因此其申请的内存大小没有限制,因此申请较大的内存时可以使用这个函数。vmalloc申请内存的过程中可以睡眠,因此不能用于中断上下文中。
对应的内存释放函数为
void vfree(const void *addr);
一般来说,为了性能,通常使用kzalloc/kmalloc分配内存,如果要分配的内存过大则使用vmalloc
示例程序
//mem_alloc.c
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int __init mem_alloc_init(void);
static void __exit mem_alloc_exit(void);
#define KMALLOC_SIZE 131072
#define KZALLOC_SIZE 131072
#define VMALLOC_SIZE 4194304 //4096 * 1024 bytes
char* 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", (unsigned long)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", (unsigned long)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", (unsigned long)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", (unsigned long)vmalloc_pt);
}
printk("#####################\n");
return 0;
}
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命令查看模块加载和卸载的输出
从输出中可以看到,在我的电脑上kmalloc和kzalloc可以成功分配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://www.toymoban.com/news/detail-595587.html
https://blog.csdn.net/lunhui2016/article/details/114297346文章来源地址https://www.toymoban.com/news/detail-595587.html
到了这里,关于Linux内核内存分配函数kmalloc、kzalloc和vmalloc的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!