【Linux】内核中申请内存的方法

在内核中申请内存和在用户空间中申请内存不同,有以下因素引起了复杂性,包括:

  • 内核的虚拟和物理地址都被限制到1GB。
  • 内核的内存不能pageable。
  • 内核通常需要连续的物理地址。
  • 通常内核申请内存是不能睡眠
  • 内核中的错误比其他地方的错误有更多的代价。

内核中申请内存有一些简单的规则:

  • 判断申请内存的时候可否睡眠,也就是调用kmalloc的时候能否被阻塞。如果在一个中断处理函数中或者有一个锁的时候,就不能被阻塞。如果在一个进程上下文,也没有锁,则一般可以睡眠。
  • 如果可以睡眠,指定GFP_KERNEL。如果不能睡眠,就指定GFP_AUTOMIC
  • 如果需要DMA可以访问的内存,比如ISA或者有些PCI设备,就需要指定GFP_DMA
  • 需要对返回值检查NULL。
  • 为了没有内存泄漏,需要用完后释放内存,且最好不要在死循环中开辟内存。

kmalloc和kfree

void *kmalloc(size_t size, gfp_t flags)
      
void kfree(const void *objp) //必须手动释放

kmalloc申请的内存位于物理内存映射区域(即虚拟内存地址)。但是在物理上它是连续的,他们与真实的物理地址只有一个固定的偏移,因为存在比较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB。

flags的参考用法:

  • 用于进程上下文,可以睡眠: GFP_KERNEL
  • 用于进程上下文,不可以睡眠( 中断处理程序、软中断和Tasklet):GFP_ATOMIC
  • 用于DMA的内存,可以睡眠:GFP_DMA | GFP_KERNEL
  • 用于DMA的内存,不可以睡眠:GFP_DMA | GFP_ATOM

devm_kmalloc和devm_kfree

devm_kmalloc分配的内存可以跟设备进行绑定,当设备跟驱动分离时,跟设备绑定的内存会被自动释放,不需要我们手动释放。当然,如果内存不再使用了,我们也可以使用函数devm_kfree手动进行释放。

/**
 * devm_kmalloc - Resource-managed kmalloc
 * @dev: Device to allocate memory for
 * @size: Allocation size
 * @gfp: Allocation gfp flags
 *
 * Managed kmalloc.  Memory allocated with this function is
 * automatically freed on driver detach.  Like all other devres
 * resources, guaranteed alignment is unsigned long long.
 *
 * RETURNS:
 * Pointer to allocated memory on success, NULL on failure.
 */
void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
{
	struct devres *dr;

	/* use raw alloc_dr for kmalloc caller tracing */
	dr = alloc_dr(devm_kmalloc_release, size, gfp);
	if (unlikely(!dr))
		return NULL;

	/*
	 * This is named devm_kzalloc_release for historical reasons
	 * The initial implementation did not support kmalloc, only kzalloc
	 */
	set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
	devres_add(dev, dr->data);
	return dr->data;
}
EXPORT_SYMBOL_GPL(devm_kmalloc);

kzalloc和kfree

kzalloc函数与kmalloc非常相似,参数及返回值是一样的,可以说前者是后者的一个变种,因为kzalloc实际上只是额外附加了__GFP_ZERO标志。所以它除了申请内核内存外,还会对申请到的内存内容清零。

void *kzalloc(size_t size, gfp_t flags)
{   
    return kmalloc(size, flags | __GFP_ZERO);
}

kzalloc()函数一般用在Linux驱动代码中的 probe 函数中芯片驱动的内存开辟操作。

devm_kzalloc和devm_kfree

static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
{
	return devm_kmalloc(dev, size, gfp | __GFP_ZERO);
}

既然devm_XXX申请的内存可以跟设备进行绑定,那我们可以在平台驱动的probe函数中调用devm_kzalloc()为平台设备申请并绑定一片设备内存,并且,这片内存会同该平台设备共存亡,不需要我们额外操心它的释放问题。

vmalloc和vfree

vmalloc函数会在虚拟内存空间给出一块连续的内存区,但这片连续的虚拟内存在物理内存中并不一定连续。由于vmalloc没有保证申请到的是连续的物理内存,因此对申请的内存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。

void *vmalloc(unsigned long size)
    
void vfree(const void *addr) //必须手动释放

vmalloc() 和 vfree() 可以睡眠,因此不能从中断上下文调用。

总结

共同点:

  • 都是用于内核空间申请内存
  • 都是以字节为单位进行分配
  • 所分配的内存,在虚拟地址上连续

不同点:

  • kzalloc是强制清零的kmalloc操作
  • kmallockzalloc分配的内存大小有限制(128KB),而vmalloc没有限制
  • kmallockzalloc可以保证分配的内存在物理地址是连续的,但是vmalloc不能保证
  • kmallockzalloc分配内存的过程可以是院子过程(使用GFP_ATOMIC),而vmalloc分配内存是则可能产生阻塞
  • kmallockzalloc分配内存的开销小,因此kmallockzallocvmalloc要快

一般情况下,内存只要在要被DMA访问的时候才需要物理上连续,但为了性能上的考虑,内核中一般使用kmalloc,而只有在需要获得大块内存时才使用vmalloc。例如,当模块被动态加载到内核当中时,就把模块装载到有vmalloc分配的内存上。

上一篇:在VMware环境下,用PE安装Windows可行吗?


下一篇:ARM嵌入式学习--第五天