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;不为空

alt text

慢速路径:

从slub中分配

alt text

从percpu page中分配

alt text

从percpu partial中分配

alt text

从node partial中分配

alt text

object释放

快速

alt text

慢速:

释放给cpu_slab->partial

alt text

释放给node

alt text

释放给伙伴系统

alt text

疑问:

__slab_free->
	-> set_freepointer(s, tail, prior);
	-> cmpxchg_double_slab
	-> return;

有个疑问就是上面的操作都是对释放的page操作,不应该page释放了之后应该把page加入kmem_cache_cpu中吗?但好像没有看见这个地方;

posted @ 2024-12-31 00:05  _xingxing  阅读(36)  评论(0)    收藏  举报