Bootstrap

Linux驱动编程 - kmalloc、vmalloc区别

目录

 

前言:

1、区别

2、使用差异

一、kmalloc、kzalloc、kfree

1、动态申请

1.1 kmalloc()

1.2 kzalloc()

2、内存释放

3、示例

二、vmalloc、vzalloc、vfree

1、动态申请

1.1 vmalloc()

1.2 vzalloc()

2、内存释放

3、示例


前言:


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

;