在内核中申请内存和在用户空间中申请内存不同,有以下因素引起了复杂性,包括:
- 内核的虚拟和物理地址都被限制到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
操作 -
kmalloc
和kzalloc
分配的内存大小有限制(128KB),而vmalloc
没有限制 -
kmalloc
和kzalloc
可以保证分配的内存在物理地址是连续的,但是vmalloc
不能保证 -
kmalloc
和kzalloc
分配内存的过程可以是院子过程(使用GFP_ATOMIC),而vmalloc
分配内存是则可能产生阻塞 -
kmalloc
和kzalloc
分配内存的开销小,因此kmalloc
和kzalloc
比vmalloc
要快
一般情况下,内存只要在要被DMA访问的时候才需要物理上连续,但为了性能上的考虑,内核中一般使用kmalloc
,而只有在需要获得大块内存时才使用vmalloc
。例如,当模块被动态加载到内核当中时,就把模块装载到有vmalloc
分配的内存上。