Linux 内存管理之 SLUB分配器(5):slub初始化过程

Linux 内存管理之 SLUB分配器(5):slub初始化过程

本小节整理记录下kmem_cache结构的初始化过程,其实呢,在前边4个小节整理完成后,本部分也就呼之欲出了

本部分主要解决如下问题:

  1. 在slub分配逻辑中可以看到,object的alloc实际是在kmem_cache和kmem_cache_node 结构中分配的,那么必然存在一个蛋与鸡的故事,如何处理的?
  2. 在初始化的过程中,有哪些关键接口,以及其作用?

首先将本部分的整体调用结构图示出来:

image-20211227203600652

1. 构造静态kmem_cache & kmem_cache_node结构

前边第3小节已经介绍了object的分配方法,object分配的时候会在kmem_cache结构中逐级搜索,即需要kmem_cache和kmem_cache_node作为前置条件,内核的做法是先填充一个kmem_cache的数据结构,然后再调用alloc接口重新分配一次,将其内容填充进来;

image-20211227204244666

  1. 创建静态的kmem_cache和kmem_cache_node结构
  2. 填充kmem_cache的结构体
    1. 填充name、size等变量
    2. 计算size、order、order_object的数据
    3. 申请node的object,注意这个时候还没有slub结构,所以调用的是early接口,直接向buddy申请page
    4. 填充node中变量
    5. 申请并填充kmem_cache_cpu结构
  3. 向slub系统申请object,并赋值添加到系统中
    1. 向slub申请object
    2. 将boot struct填充到刚才申请的空间中
    3. 添加到slab_caches的链表上

这里边几个重要的接口:

接口说明
create_boot_cache填充kmem_cache结构体
kmem_cache_open实际填充kmem_cache的操作
calculate_sizes计算object size并构造object结构
init_kmem_cache_nodes(s)初始化node结构
early_kmem_cache_node_alloc(node)向buddy申请page作为node结构,并赋值
alloc_kmem_cache_cpus(s)申请并构造cpu结构
bootstrap申请object将kmem_cache填充进来并添加到系统
kmem_cache_zalloc通过slub系统申请object结构

经过这部分,我们可以知道的是后续构造空间的接口顺序是:

  1. kmem_cache_zalloc 申请空间
  2. create_boot_cache 填充数据
  3. list_add增加到链表管理

此部分比较重要的接口:

static void early_kmem_cache_node_alloc(int node)
{
	struct page *page;
	struct kmem_cache_node *n;

	page = new_slab(kmem_cache_node, GFP_NOWAIT, node);//直接向slub申请object,此接口实际调用allocate_page,之前分析过
	
    n = page->freelist;//接下来几行都是赋值操作
	page->freelist = get_freepointer(kmem_cache_node, n);
	page->inuse = 1;
	page->frozen = 0;
	kmem_cache_node->node[node] = n;

	init_object(kmem_cache_node, n, SLUB_RED_ACTIVE);//填充object数据,这里实际填充的是inactive值
	init_tracking(kmem_cache_node, n);

	kasan_kmalloc(kmem_cache_node, n, sizeof(struct kmem_cache_node), GFP_KERNEL);
	init_kmem_cache_node(n);//填充node

    inc_slabs_node(kmem_cache_node, node, page->objects);//node结构中几个统计数值++
	__add_partial(n, page, DEACTIVATE_TO_HEAD);//添加到partial中
}

static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache)
{
	int node;
	struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);//cache和node已经ready可以alloc了
	struct kmem_cache_node *n;

	memcpy(s, static_cache, kmem_cache->object_size);//拷贝过来

	__flush_cpu_slab(s, smp_processor_id());
	for_each_kmem_cache_node(s, node, n) {
		struct page *p;
		list_for_each_entry(p, &n->partial, lru)//partial链表赋值
			p->slab_cache = s;
		list_for_each_entry(p, &n->full, lru)//full链表赋值
			p->slab_cache = s;
	}
	slab_init_memcg_params(s);
	list_add(&s->list, &slab_caches);//slab_caches ++
	return s;
}

2. kmalloc_caches中的几个计算

setup_kmalloc_cache_index_table() size_index的填充

size(bytes)Indexsize_index[index]
803
1613
3233
6477
96117
128157
192238

对应到kmalloc_cache上

ikmalloc_cache[i]
38
416
532
664
7128
8256
9512
101024
112048

对应上上述的size_index:

size(bytes)Indexsize_index[index]kmalloc_slab(size)kmalloc_cache[i]
803kmalloc_cache[3]8
1613kmalloc_cache[3]8
3233kmalloc_cache[3]8
6477kmalloc_cache[7]128
96117kmalloc_cache[7]128
128157kmalloc_cache[7]128
192238kmalloc_cache[8]256

这部分计算比较奇怪,size_index数组填充为上述值后,反倒不比原始数据更容易理解:

 */
static s8 size_index[24] = {
	3,	/* 8 */
	4,	/* 16 */
	5,	/* 24 */
	5,	/* 32 */
	6,	/* 40 */
	6,	/* 48 */
	6,	/* 56 */
	6,	/* 64 */
	1,	/* 72 */
	1,	/* 80 */
	1,	/* 88 */
	1,	/* 96 */
	7,	/* 104 */
	7,	/* 112 */
	7,	/* 120 */
	7,	/* 128 */
	2,	/* 136 */
	2,	/* 144 */
	2,	/* 152 */
	2,	/* 160 */
	2,	/* 168 */
	2,	/* 176 */
	2,	/* 184 */
	2	/* 192 */
};

这里存疑

3. 关键调用逻辑整理记录

 mm_init
 	-- kmem_cache_init() //注意此部分是在mem_init之后就进行了,说明在此之前页表转换系统和buddy系统已经完成了
 		-- create_boot_cache();// kmem_cache_node,创建静态的kmem_cache
 			-- s->xxx = xxx //赋值操作
 			-- __kmem_cache_create(s, flags)
 				-- kmem_cache_open(s, flags)
 					-- calculate_sizes(s, -1) //计算object的size,然后构造oo
 					-- set_min_partial(s, ilog2(s->size) / 2) 
                		//从这里可以看到partial的数量与size相关,8Byte的话1个,8K的话6个
 					-- s->cpu_partial = 0 /2 /6/ 13/ 30同样与size相关
 					-- init_kmem_cache_nodes(s) 
 						-- for_each_node_state
 							-- slab_state == down ==> early_kmem_cache_node_alloc
 								-- page = new_slab //向buddy申请page,注意再次之前已经计算出order、count、size了;
 									//inuse 此时为objects数量,frozen此时为1
 								-- page->xxx赋值
 								-- init_object 
                					//在申请page的时候allocate中已经操作过了一遍,差别在于
                					//那时候传入为inactive,此时为active,即此时为分配出去的;
 								-- init_tracking
 								-- init_kmem_cache_node(n)//创建两个链表,将partial、nr_slabs、total_objects设置为0
                					//标准初始化处理
 								-- inc_slabs_node(kmem_cache_node)		//nr_slab数量+1,objects总数添加
 								-- __add_partial(n, page, DRACTIVATE_TO_TAIL) //添加到partial列表中
 					-- alloc_kmem_cache_cpus
 						-- s->cpu_slab = __alloc_percpu()
 						-- init_kmem_cache_cpus(s)
 							-- for_each_possible_cpu(cpu)//遍历每个cpu,假设4核咯
 								-- per_cpu_ptr(s->cpu_slab, cpu)->tid = init_tid(cpu));
 				-- sysfs_slab_add(s) //添加到sys文件系统中
 		-- bootstrap()//申请个object 将上述静态kmem_cache丢进来
 			-- kmem_cache *s = kmem_cahce_zalloc(kmem_cache, GFP_NOWAIT)//申请空间,注意此时已经创建完成两个static的结构:kmem_cache & kmem_cache_node
 				-- kmem_cache_alloc(kmem_cache, GFP_NOWAIT | __GFP_ZERO)
 					-- void *ret = slab_alloc(s, gfpflags, _RET_IP_) //分配kmem_cache_node这么大的object出来,这部分在第三小节有详细逻辑描述,这里简单过去了
 						-- slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr) //把node加进来,注意这个位置传入的是NUMA_NO_NODE
 							-- slab_pre_aloc_hook(s, gfpflags)
 							-- object = c->freelist,page = c->page
 							-- if(unlikely(!object || !node_match(page, node)))//判断cpu指向的freelist是否有空闲的object
 								-- case1 可以找到:
 									-- void* next_object= get_freepointer_safe(s, object)
 									-- unlikely(!this_cpu_cmpchg_double(s->cpu_slab->freelist, s->cpu_slab->tid, object, tid, next_object, next_tid(tid))) //比较并赋值
 									-- prefetch_freepointer(s, next_object);//把next_object 预取进来
 								-- case2 无法找到:object = __slab_alloc(s, gfpflags, node, addr, c)
 									-- 加个锁
 									-- ___slab_alloc(s, gfpflags, node, addr, c)
 										-- 1. page内分配,针对于刚好有释放的情况;
 										-- 2. cpu->partial内分配
 										-- 3. node 内分配
 										-- 4. 直接申请page
 									-- 去掉锁
 							-- slab_post_alloc_hook(s, gfpflags, 1 , &object)
 					-- trace_kmem_cache_alloc()//添加trace操作,
 			-- memcpy(s, static_cache, kmem_cache->object_size )
 			-- __flush_cpu_slab(s, smp_processor_id)())
 			-- p->slab_cache = s //对分配到的每个page赋值
 			-- list_add(&s->list, &slab_caches) //将这个kmem_cache加入链表中
 		-- setup_kmalloc_cache_index_table() //填充size_index数组,这个数组在kmalloc_caches初始化的时候会使用
 		-- create_kmalloc_caches(0)
 			-- 遍历kmalloc_caches数组
 				-- new_kmalloc_cache(i, flags)
 					-- kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name, kmalloc_info[idx].size, flags)
 						-- kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT)	//前文也已经分析过了,分配object操作;
 						-- create_boot_cache(s, name, size, flags)//前文已经分析过了,是赋值操作
 						-- list_add(&s->list, &slab_caches)

4. 附录

  1. 相关目录

    目录说明
    /init/main.ckernel初始化
    /mm/slub.cslub 结构和操作核心文件
    /include/linux/slub_def.h相关结构体定义
    /mm/slab_common.ckmalloc 相关结构体
  2. 最近这几部分的整理总觉得串不起来,到现在也基本都捋顺了,后续再重新串一遍搞个综述出来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值