内核中,分别对于不同的应用层面,对 kmalloc 有不同的定义,分别位于
slab.c
Mark Hemment 捣鼓的,从从 Sun OS 引进
slob.c
针对嵌入式系统,主要是针对内存非常有限的设备
slub.c
可以说是对 slab 的重新设计,代码量更下,并且能够更好的适应 large NUMA 系统 , 被认为时 slab 和 slob 的取代者
Slob 中的 kmalloc 只有一个语句, return __kmalloc_node(size, flags, -1); 跟踪相应的 __kmalloc_node() 函数,该函数首先会获得 ARCH_KMALLOC_MINALIGN 和 ARCH_SLAB_MINALIGN 中的 max 值,当需要分配的页面大小小于 PAGE_SIZE 减去 max 的差时:
1、 当 size 为 0 时,返回 ZERO_SIZE_PTR 错误
2、 采用 slob_alloc 分配内存,不成功时返回错误
3、 返回 size+max 的地址
否则,调用 slob_new_page 重新分配一个新的内存空间,并且将分配的虚地址转化成的物理地址交给一个 page->private 的值为所分配的大小的页面。
在看 Slub 和 slab 中的 kmalloc 的时候先要了解 __builtin_constant_p 是一个 gcc 的内建函数用于判断一个值是否为编译时常数,如果参数 EXP 的值是常数,函数返回 1 ,否则返回 0 。
Slub 中,首先判断 kmalloc 函数中的 size 的类型,如果是在编译时不确定,那么直接调用 __kmalloc(size, flags) 并且取值返回,否则,将在返回之前 进行一些判断:
1、 当需分配的的大小大于页面大小时,使用 __kmalloc(size, flagkmalloc_larges) 进行分配
2、 当分配时带有 SLUB_DMA 标记时,首先采用 kmalloc_slab(size) 分配内存,分配失败时,返回 ZERO_SIZE_PTR
3、 返回 kmem_cache_alloc(s, flags)
__kmalloc(size, flags)
1、 当 size 不大于 PAGE_SIZE 时,调用 kmalloc_large(size, flags) 分配空间并返回
2、 当 get_slab(size, flags) 返回值不为 0 或者空时,返回其返回值
3、 调用 slab_alloc(s, flags, -1, __builtin_return_address(0)) 分配空间
kmalloc_large(size, flags) 调用 __get_free_pages(flags | __GFP_COMP, get_order(size)); 返回指定大小的空闲页面
kmalloc_slab() 当 kmalloc_index(size) 返回的索引不为 0 时,调用 kmalloc_cache(index) 分配页面
kmem_cache_alloc() 返回 slab_alloc() 给分配的空间。
Slab 中的 kmalloc() 分配策略类似于 sulb 中,都需要对 size 的类型进行判断, 如果是在编译时不确定,那么直接调用 __kmalloc(size, flags) 并且取值返回,否则,将在返回之前进行一些判断:
1、 错误处理,当 size 为 0 时,返回 ZERO_SIZE_PTR
2、 当 cache 的值小于 size 时, i+=1, 否则跳到 found 处执行
如果在编译选项中设置了 CONFIG_ZONE_DMA ,并且标明了 GFP_DMA ,调用 kmem_cache_alloc(malloc_sizes[i].cs_dmacachep, flags) 分配内存并返回。否则调用 kmem_cache_alloc(malloc_sizes[i].cs_cachep, flags); 分配内存并返回
__kmalloc(size, flags)
返回 __do_kmalloc(size, flags, NULL); 的值
kmem_cache_alloc
返回 __cache_alloc(cachep, flags, __builtin_return_address(0)) 的值
__do_kmalloc()
得到一个 cache 的指针,如果指针不为空或者 0 的时候,返回指针,否则调用 __cache_alloc(cachep, flags, caller) 分配一个缓存并返回
__cache_alloc()
该函数主要功能是分配一个
1、 得到一个 CPU 的对应的 cache 数组,设置为变量 ac
2、 当 ac 可用时, cache 指针加 1 并且状态设置成命中 , 返回 ac 的入口指针
3、 或者将 cache 指针加 1 ,返回重新分配并填充的 cache 指针