slab
为什么要有slub
伙伴子系统是以4k * 2^order分配内存,往往驱动分配内存用不了4k,必然会有大量的内存碎片,为了解决这一个问题,提出了slub;
slub能分配多大的内存?从slub中想分配多大就多大吗?
- 从slub中分配的内存大小是固定的,在
kmem_cache_create
中传入参数时,固定好了,只能那么大; - 当然可以创建不同的slub,每个slub 指定object为不同的大小;
以实际代码为例:
- slub其实是一个object内存池,这个内存池只能分配object那么大的内存;
例如,下述代码,创建了一个mycache
的内存池,指定了object的大小是size
;
my_cache = kmem_cache_create("mycache", size, 0,
SLAB_HWCACHE_ALIGN, NULL);
那么后续使用kmem_cache_alloc
分配内存时,得到的内存大小都是size这么大,没有其他任何一个值;
(之前一直被误导,都在介绍slab以byte为单位管理,以为创建了一个kmem_cache之后
,可以任意分配size个byte呢,其实不是的,之后只能申请到固定size
这么大的内存了)
关键理解
freelist的两个作用:
object = c->freelist;
kmem_cache_cpu
中的freelist本身就是一个obdject;get_freepointer(s, freelist);
->(void *)*(unsigned long *)(ptr_addr)
;同时这个freelist中的内容是下一个空闲object的地址;
如何控制分配出来的是object大小?
比如第一次:
new_slab_objects -> page = new_slab(s, flags, node);
-> freelist = page->freelist;
-> c->page = page;
-> return freelist;
c->freelist = get_freepointer(s, freelist);
- 这个freelist是从page中得到的;
- 之后就可以按照
get_freepointer(s, freelist);
->(void *)*(unsigned long *)(ptr_addr)
分配object了; - 分配object的原理是这个freelist中的内容是下一个object的地址,是怎么按照object 的size 准确控制这个地址的呢?
见如下代码:
if (!shuffle) {
start = fixup_red_left(s, start);
start = setup_object(s, page, start);
page->freelist = start; /* 更新freelist为page的首地址 */
for (idx = 0, p = start; idx < page->objects - 1; idx++) {
next = p + s->size; /* 根据size找到next */
next = setup_object(s, page, next);
set_freepointer(s, p, next); /* 设置freelist,这样就形成了一个单链表 */
p = next; /* 更新为next */
}
根据object的size进行划分,形成了一个单链表
我应该为这个slab分配多少个page?
object 分配
if (rem <= slab_size / fract_leftover): /* 如果浪费空间在容忍范围直接返回 */
- 设置了slab期望最小的object数,min_objects = min(min_objects, max_objects);
- 设置了slab大小对应的最大order:max_order
- 在最小slab size对应的order(最小object) ~ 最大order中for循环计算,空间浪费在容忍范围之内即可;
分配思想
简而言之有两个分配类:
- percpu:struct kmem_cache_cpu __percpu *cpu_slab;
- freelist
- page
- partial
- node:struct kmem_cache_node *node[MAX_NUMNODES];
- struct list_head partial;
- 也就是会从percpu或者node中分配;
- percpu中有三条分配路径,freelsit,page,partial,或者直接从buddy子系统中分配内存;
- node中有partial链表分配路径;
快速路径:freelist,object = c->freelist;不为空
慢速路径:
从slub中分配
从percpu page中分配
从percpu partial中分配
从node partial中分配
object释放
快速
慢速:
释放给cpu_slab->partial
释放给node
释放给伙伴系统
疑问:
__slab_free->
-> set_freepointer(s, tail, prior);
-> cmpxchg_double_slab
-> return;
有个疑问就是上面的操作都是对释放的page操作,不应该page释放了之后应该把page加入kmem_cache_cpu中吗?但好像没有看见这个地方;