kmalloc和kfree
kmalloc
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
if (__builtin_constant_p(size)) {
if (size > KMALLOC_MAX_CACHE_SIZE)
return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
if (!(flags & GFP_DMA)) {
int index = kmalloc_index(size);
if (!index)
return ZERO_SIZE_PTR;
return kmem_cache_alloc_trace(kmalloc_caches[index],
flags, size);
}
#endif
}
return __kmalloc(size, flags);
}
参数
kmalloc
的第一个参数是要分配的块的大小,第二个参数是分配标志。
size 参数:
用户空间malloc
是基于堆内存分配,内核负责管理系统物理内存,物理内存只能按页面进行分配,因此,kmalloc
是基于页进行分配。另外需要注意的一点是内核只能分配一些预定义的、固定 大小的字节数组。kmalloc
可以处理的最小的内存块是32或64,最大分配的内存大小为128K。
flags参数:
GFP_KERNEL
内核内存的通常分配方法,可能会引起休眠。
GFP_USER
用于为用户空间分配内存,可能会休眠。
GFP_ATOMIC
用于在中断处理例程或其他运行于进程上下文之外的代码中分配内存,不会休眠。
GFP_HIGHUSER
用于为用户空间分配内存,指高端内存分配,可能会休眠。
GFP_NOIO
禁止任何I/O的初始化(主要在虚拟内存代码中使用)。
GFP_NOFS
分配不允许执行任何文件系统调用(主要在文件系统代码中使用)。
分割线以上的flag可以和分割线以下的flag “或”起来使用(经常可以在判断条件里看到)
__GFP_DMA
该标志请求分配发生在可进行DMA的内存区段中。
__GFP_HIGHMEM
该标志表明要分配的内存可位于高端内存。
__GFP_NOWAPN
该标志使用的次数较少,它主要是避免内核在无法满足分配请求时产生警告信息。
__GFP_COLD
该标志表示请求尚未使用的“冷”页面。
__GFP_HIGH
该标记标记了一个高优先级的请求,它允许为紧急状况而消耗由内核保留的最后一些页面。
__GFP_REPEAT
该标志表示在分配器在满足分配请求而遇到困难时,“努力再尝试一次”,它会重新尝试分配,但还是有失败的可能性。
__GFP_NOFAIL
该标志表示在分配器在满足分配请求而遇到困难时告诉分配器始终不返回失败。
__GFP_NORETRY
该标志表示再请求内存不可获得的时候会立即返回。
GFP_前缀是由于在分配内存时总是调用get_free_page来实现实际的分配而得来的缩写。
解读
下面开始详细解读这个函数:
首先是一个Gcc的内建函数 __builtin_constant_p
用于判断一个值是否为编译时常数,如果参数EXP 的值是常数,函数返回 1,否则返回 0。如果size为常数,就开始判断它是否超过了我们实际使用slab缓存的最大大小,如果超过了,就调用kmalloc_large
进行大内存分配,实际上最终会调用页分配器去分配内存,而不是使用slab分配器。如下:
static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
{
unsigned int order = get_order(size);
return kmalloc_order_trace(size, flags, order);
}
调用kmalloc_order_trace
static __always_inline void *
kmalloc_order_trace(size_t size, gfp_t flags, unsigned int order)
{
return kmalloc_order(size, flags, order);
}
调用kmalloc_order
void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{
void *ret;
struct page *page;
flags |= __GFP_COMP;
page = alloc_pages(flags, order);
ret = page ? page_address(page) : NULL;
kmemleak_alloc(ret, size, 1, flags);
kasan_kmalloc_large(ret, size, flags);
return ret;
}
EXPORT_SYMBOL(kmalloc_order);
可以看到实际上是使用alloc_pages去分配内存
再往下看:flags & GFP_DMA
,如果请求分配发生在可进行DMA的内存区段中。kmalloc_index
得出这个size的分配属于哪个slab,会返回一个索引,比如0代表zero alloc,1代表65 … 96 bytes等。如果索引值为0,调用ZERO_SIZE_PTR
返回地址16所指向的内容,可以像NULL一样传递给kfree,实际上就是空。如果索引值是其他值,调用kmem_cache_alloc_trace
这里是实际上的申请语句
static __always_inline void *kmem_cache_alloc_trace(