栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux内核源码分析之slab(二)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux内核源码分析之slab(二)

目录

结构体分析

结构体之间关系

静态初始化

创建缓存


结构体分析

kmem_cache 每个缓存由kmem_cache结构的一个实例表示。

struct kmem_cache {
	//是每个CPU一个array_cache类型的变量
	struct array_cache __percpu *cpu_cache;


	unsigned int batchcount;//指定了在per-CPU列表为空的情况下,从缓存的slab中获取对象的数目。它还表示在缓存增长时分配的对象数目
	unsigned int limit;//指定per-CPU列表中保存的对象的最大数目
	unsigned int shared;

	unsigned int size;//cache大小
	struct reciprocal_value reciprocal_buffer_size;


	slab_flags_t flags;		
	unsigned int num;		


	
	unsigned int gfporder;//分配内存页面的order

	
	gfp_t allocflags;

	size_t colour;			
	unsigned int colour_off;	
	struct kmem_cache *freelist_cache;
	unsigned int freelist_size;

	
	void (*ctor)(void *obj);


	const char *name;//slab名字
	struct list_head list;//所有slab链接
	int refcount;//引用计数
	int object_size;//对象大小
	int align;//对齐大小

	int obj_offset;
	//指向管理kmemcache的上层结构
	struct kmem_cache_node *node[MAX_NUMNODES];
};

array_cache 缓存

struct array_cache {
	unsigned int avail;//当前可用对象的数目
	unsigned int limit;
	unsigned int batchcount;
	unsigned int touched;//从缓存移除一个对象时,将touched设置为1,而缓存收缩时,则将touched设置0
	void *entry[];	//对象地址
};

avail保存了当前可用对象的数目。在从缓存移除一个对象时,将touched设置1,而缓存收缩时,将touched设置为0

kmem_cache_node节点

struct kmem_cache_node {
	spinlock_t list_lock;//自旋锁

#ifdef CONFIG_SLAB
	struct list_head slabs_partial;	
	struct list_head slabs_full;//对象被占用的slab描述符链表
	struct list_head slabs_free;//只包含空闲对象的slab描述符链表
	unsigned long total_slabs;	
	unsigned long free_slabs;	
	unsigned long free_objects;
	unsigned int free_limit;
	unsigned int colour_next;	
	struct array_cache *shared;	
	struct alien_cache **alien;	
	unsigned long next_reap;	
	int free_touched;		
#endif
};

三种缓存 空闲,满,部分空闲。

结构体之间关系

静态初始化

        伙伴系统已经完全启用,初始化slab数据结构,内核需要若干小于一整页的内存块,适合用kmalloc分配,但在slab启用后才能使用kmalloc。

        kmem_cache_init函数用于初始化slab,在伙伴系统启用之后调用

static struct kmem_cache kmem_cache_boot = {
	.batchcount = 1,
	.limit = BOOT_CPUCACHE_ENTRIES,
	.shared = 1,
	.size = sizeof(struct kmem_cache),
	.name = "kmem_cache",
};


void __init kmem_cache_init(void)
{
	int i;
	//指向静态定义的kmem_cache_boot
	kmem_cache = &kmem_cache_boot;

	if (!IS_ENABLED(CONFIG_NUMA) || num_possible_nodes() == 1)
		use_alien_caches = 0;

	for (i = 0; i < NUM_INIT_LISTS; i++)
		kmem_cache_node_init(&init_kmem_cache_node[i]);

	if (!slab_max_order_set && totalram_pages() > (32 << 20) >> PAGE_SHIFT)
		slab_max_order = SLAB_MAX_ORDER_HI;


   //建立保存kmem_cache结构的kmem_cache
	
	create_boot_cache(kmem_cache, "kmem_cache",
		offsetof(struct kmem_cache, node) +
				  nr_node_ids * sizeof(struct kmem_cache_node *),
				  SLAB_HWCACHE_ALIGN, 0, 0);

    //加入全局slab_caches
	list_add(&kmem_cache->list, &slab_caches);
	memcg_link_cache(kmem_cache, NULL);
	slab_state = PARTIAL;

	kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE] = create_kmalloc_cache(
				kmalloc_info[INDEX_NODE].name[KMALLOC_NORMAL],
				kmalloc_info[INDEX_NODE].size,
				ARCH_KMALLOC_FLAGS, 0,
				kmalloc_info[INDEX_NODE].size);
	slab_state = PARTIAL_NODE;
	setup_kmalloc_cache_index_table();

	slab_early_init = 0;

	{
		int nid;
        //加入全局slab_cache链表中
		for_each_online_node(nid) {
			init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid);

			init_list(kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE],
					  &init_kmem_cache_node[SIZE_NODE + nid], nid);
		}
	}
    //建立kmalloc函数使用的kmem_cache
	create_kmalloc_caches(ARCH_KMALLOC_FLAGS);
}

        kmem_cache_init创建系统中第一个slab缓存,以便为kmem_cache的实例提供内存,静态数据结构。初始化时建立了第一个 kmem_cache 结构之后,init_list 函数负责一个个分配内存空间。

static void __init init_list(struct kmem_cache *cachep, struct kmem_cache_node *list,
				int nodeid)
{
	struct kmem_cache_node *ptr;
	//分配新的kmem_cache_node结构的空间
	ptr = kmalloc_node(sizeof(struct kmem_cache_node), GFP_NOWAIT, nodeid);
	BUG_ON(!ptr);
	//复制初始时的静态kmem_cache_node结构
	memcpy(ptr, list, sizeof(struct kmem_cache_node));
	spin_lock_init(&ptr->list_lock);

	MAKE_ALL_LISTS(cachep, ptr, nodeid);
	//设置kmem_cache_node的地址
	cachep->node[nodeid] = ptr;
}

kmalloc_caches是kmalloc_cache的集合

初始化过程,遍历枚举

#define KMALLOC_SHIFT_HIGH	((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? 
				(MAX_ORDER + PAGE_SHIFT - 1) : 25)


enum kmalloc_cache_type {
	KMALLOC_NORMAL = 0,
	KMALLOC_RECLAIM,
#ifdef CONFIG_ZONE_DMA
	KMALLOC_DMA,
#endif
	NR_KMALLOC_TYPES
};

extern struct kmem_cache *
kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1];


void __init create_kmalloc_caches(slab_flags_t flags)
{
	int i;
	enum kmalloc_cache_type type;

	for (type = KMALLOC_NORMAL; type <= KMALLOC_RECLAIM; type++) {
		for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
			if (!kmalloc_caches[type][i])
				new_kmalloc_cache(i, type, flags);

			if (KMALLOC_MIN_SIZE <= 32 && i == 6 &&
					!kmalloc_caches[type][1])
				new_kmalloc_cache(1, type, flags);
			if (KMALLOC_MIN_SIZE <= 64 && i == 7 &&
					!kmalloc_caches[type][2])
				new_kmalloc_cache(2, type, flags);
		}
	}

	
	slab_state = UP;
#ifdef CONFIG_ZONE_DMA
	for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
		struct kmem_cache *s = kmalloc_caches[KMALLOC_NORMAL][i];

		if (s) {
			kmalloc_caches[KMALLOC_DMA][i] = create_kmalloc_cache(
				kmalloc_info[i].name[KMALLOC_DMA],
				kmalloc_info[i].size,
				SLAB_CACHE_DMA | flags, 0, 0);
		}
	}
#endif
}

创建缓存

static size_t calculate_slab_order(struct kmem_cache *cachep,
				size_t size, slab_flags_t flags)
{
	size_t left_over = 0;
	int gfporder;

	//每次order+1. 从第一页帧开始,每次倍增slab长度
	//1) 8*left_over 小于slab长度,即浪费空间小于1/8
	//2)gfp_order大于或等于slab_max_order
	for (gfporder = 0; gfporder <= KMALLOC_MAX_ORDER; gfporder++) {
		unsigned int num;
		size_t remainder;
		//找到一个slab布局,size对象长度,gfp_order页阶,num slab上对象数目
		num = cache_estimate(gfporder, size, flags, &remainder);
		if (!num)
			continue;

		if (num > SLAB_OBJ_MAX_NUM)
			break;

		//slab头在管理数据存储在slab之外
		if (flags & CFLGS_OFF_SLAB) {
			struct kmem_cache *freelist_cache;
			size_t freelist_size;

			freelist_size = num * sizeof(freelist_idx_t);
			freelist_cache = kmalloc_slab(freelist_size, 0u);
			if (!freelist_cache)
				continue;


			if (OFF_SLAB(freelist_cache))
				continue;

			if (freelist_cache->size > cachep->size / 2)
				continue;
		}
		cachep->num = num;
		cachep->gfporder = gfporder;
		left_over = remainder;

		if (flags & SLAB_RECLAIM_ACCOUNT)
			break;

		if (gfporder >= slab_max_order)
			break;

		if (left_over * 8 <= (PAGE_SIZE << gfporder))
			break;
	}
	return left_over;
}

  如果下述条件之一成立,即结束循环

  1. 每次order+1. 从第一页帧开始,每次倍增slab长度
  2. 8*left_over 小于slab长度,即浪费空间小于1/8
  3. gfp_order大于或等于slab_max_order        

        内核试图通过calculate_slab_order实现的迭代过程,找到理想的slab长度。基于给定对象长度, cache_estimate针对特定的页数,来计算对象数目、浪费的空间、着色所需的空间。该函数会循环调用,直至内核对结果满意为止。

//产生per-CPU缓存
static int __ref setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
{
	if (slab_state >= FULL)
		return enable_cpucache(cachep, gfp);

	cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1);
	if (!cachep->cpu_cache)
		return 1;

...
	return 0;
}
static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
{
...
	if (cachep->size > 131072)
		limit = 1;
	else if (cachep->size > PAGE_SIZE)
		limit = 8;
	else if (cachep->size > 1024)
		limit = 24;
	else if (cachep->size > 256)
		limit = 54;
	else
		limit = 120;

	shared = 0;
	if (cachep->size <= PAGE_SIZE && num_possible_cpus() > 1)
		shared = 8;

	batchcount = (limit + 1) / 2;//缓存中对象数的一半
skip_setup://初始化数据结构
	err = do_tune_cpucache(cachep, limit, batchcount, shared, gfp);
end:
	if (err)
		pr_err("enable_cpucache failed for %s, error %dn",
		       cachep->name, -err);
	return err;
}

        为各个处理器分配所需的内存:一个array_cache的实例和一个指针数组,数组项数目
在上述的计算中给出;并初始化数据结构,这些任务委托给do_tune_cpucache。
 

参考

《深入Linux内核架构》

https://course.0voice.com/v1/course/intro?courseId=2&agentId=0


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/880075.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号