目录
前言:
Linux内核空间一般使用 kmalloc()、kzalloc()、vmalloc() 、vzalloc() 动态申请内存。下面记录如何使用。
1、区别
- vmalloc从高端内存开始分配,当内存不够时才分配低端内存。kmalloc从低端内存开始分配;
- vmalloc和kmalloc分配的虚拟地址都是连续的,但是vmalloc分配的物理地址一般不连续,kmalloc分配的物理地址连续;
- vmalloc分配的一般是大块的内存,而kmalloc一般分配的是小块内存(不超过128k);
函数 | 位置 | 特性 | 大小限制 |
---|---|---|---|
kmalloc | 低端内存(线性映射)区域 | 物理地址虚拟地址均连续 | 不能超过128K |
kzalloc | 低端内存(线性映射)区域 | 物理地址虚拟地址均连续 | 不能超过128K |
vmalloc | 高端内存(动态映射)区域 | 虚拟地址连续,物理地址不一定连续 | 无限制 |
vzalloc | 高端内存(动态映射)区域 | 虚拟地址连续,物理地址不一定连续 | 无限制 |
2、使用差异
- kmalloc 分配内存的过程可以是原子过程(使用 GFP_ATOMIC),而 vmalloc 分配内存时则可能产生阻塞;
- kmalloc 分配内存的开销小,因此 kmalloc 比 vmalloc 要快;
注意:一般情况下,内存只有在要被 DMA 访问的时候才需要物理上连续,但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用 vmalloc()。例如,当模块被动态加载到内核当中时,就把模块装载到由 vmalloc() 分配的内存上。
一、kmalloc、kzalloc、kfree
1、动态申请
1.1 kmalloc()
/* include/linux/slab.h */
void *kmalloc(size_t size, gfp_t flags);
功能: kmalloc() 申请的内存位于物理内存映射(低端内存)区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB。
参数:
- size:申请内存大小
- flags:分配标志
(1)flags 常用值
GFP_ATOMIC | 分配内存的过程是一个原子过程,分配内存的过程不会被(高优先级进程或中断)打断 |
GFP_KERNEL | 正常分配内存 |
GFP_DMA | 给 DMA 控制器分配内存,需要使用该标志(DMA要求分配虚拟地址和物理地址连续) |
(2)flags 用法
|– 进程上下文,可以睡眠 GFP_KERNEL
|– 进程上下文,不可以睡眠 GFP_ATOMIC
| |– 中断处理程序 GFP_ATOMIC
| |– 软中断 GFP_ATOMIC
| |– Tasklet GFP_ATOMIC
|– 用于DMA的内存,可以睡眠 GFP_DMA | GFP_KERNEL
|– 用于DMA的内存,不可以睡眠 GFP_DMA | GFP_ATOMIC
如果进程上下文允许睡眠情况下尽量用 GFP_KERNEL, 如果进程上下文禁止休眠的话(如中断,taskletd等)必须用 GFP_ATOMIC。
返回值:
- 非NULL:申请到的内存虚拟地址(指向一块物理上连续的内存区域);
- NULL:申请失败;
1.2 kzalloc()
kzalloc() 函数与 kmalloc() 非常相似,参数及返回值是一样的。kzalloc() 实际上只是额外附加了__GFP_ZERO标志。所以它除了申请内核内存外,还会对申请到的内存内容清零。
/* include/linux/slab.h */
static inline void *kzalloc(size_t size, gfp_t flags)
{
return kmalloc(size, flags | __GFP_ZERO);
}
2、内存释放
kmalloc() 和 kzalloc() 的内存释放函数都是 kfree()
/* mm/slob.c */
void kfree(const void *objp);
参数:
- objp:kmalloc() 和 kzalloc() 申请的内存首地址
3、示例
/* 申请内存 */
pBuffer = kmalloc(1024, GFP_KERNEL);
if (!pBuffer) {
printk(KERN_ERR "kmalloc failed\n");
return -1;
}
/* 释放内存 */
kfree(pBuffer);
二、vmalloc、vzalloc、vfree
注意:vmalloc和vfree可以休眠,因此中断上下文禁止使用
1、动态申请
1.1 vmalloc()
/* mm/vmalloc.c */
void *vmalloc(unsigned long size)
{
return __vmalloc_node_flags(size, NUMA_NO_NODE,
GFP_KERNEL);
}
功能:vmalloc() 函数则会在虚拟内存(高端内存)区域给出一块连续的内存区,但这片连续的虚拟内存在物理内存中并不一定连续。由于 vmalloc() 没有保证申请到的是连续的物理内存,因此对申请的内存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。
参数:
- size:申请内存的大小
返回值:
- 非NULL:申请到的内存的虚拟地址;
- NULL:申请失败;
1.2 vzalloc()
vzalloc比vmalloc步骤多了一步,将申请的逻辑地址连续的内存数据置为0
/* mm/vmalloc.c */
void *vzalloc(unsigned long size)
{
return __vmalloc_node_flags(size, NUMA_NO_NODE,
GFP_KERNEL | __GFP_ZERO);
}
2、内存释放
vmalloc() 和 vzalloc() 的内存释放函数都是 vfree()
void vfree(const void *addr);
3、示例
/* 申请内存 */
pBuffer = vmalloc(1024);
if (!pBuffer ) {
printk(KERN_ERR "vmalloc failed\n");
return -1;
}
/* 释放内存 */
vfree(pBuffer);