`
cloudtech
  • 浏览: 4611244 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

Linux内存管理 Slab分配器(二:初始化)

 
阅读更多

初看起来,slab系统的初始化不是特别麻烦,因为伙伴系统已经完全启用,内核没有受到其他特别的限制。尽管如此,由于slab分配器的结构所致,这里有一个鸡与蛋的问题。为初始化slab数据结构,内核需要若干远小于一整页的内存块,这些最适合由kmalloc分配。这里是关键所在:只有在slab系统已经启用之后,才能使用kmalloc。更确切的说,该问题涉及kmalloc的Per-CPU缓存的初始化。在这些缓存能初始化之前,kmalloc必须可以用来分配所需的内存空间,而kmalloc自身也处于初始化的过程中,所以这是一个不可能完成的场景,内核必须借助一些技巧。内核中使用kmem_cache_init函数用于初始化slab分配器。它在内核初始化阶段(start_kernel)、伙伴系统启用之后调用。kmem_cache_init采用了一个多步骤的过程,逐步激活slab分配器。

kmem_cache_init可以分为六个阶段:
第一个阶段:是根据kmem_cache来设置cache_cache的字段值;
第二个阶段:首先是创建arraycache_init对应的高速缓存,同时也是在这个kmem_cache_create的调用过程中,创建了用于保存cache的kmem_cache的slab,并初始化了slab中的各个对象;
第三个阶段:创建kmem_list3对应的高速缓存,在这里要注意的一点是,如果sizeof(arraycache_t)和sizeof(kmem_list3)的大小一样大,那么就不再使用kmem_cache_create来为kmem_list3创建cache了,因为如果两者相等的话,两者就可以使用同一个cache;
第四个阶段:创建并初始化所有的通用cache和dma cache;
第五个阶段:创建两个arraycache_init对象,分别取代cache_cache中的array字段和malloc_sizes[INDEX_AC].cs_cachep->array字段;
第六个阶段:创建两个kmem_list3对象,取代cache_cache中的kmem_list3字段和malloc_sizes[INDEX_AC].cs_cachep->nodelist3字段.如此一来,经过上面的六个阶段后,所有的初始化工作基本完成了。

kmem_cache_init源代码详细分析如下:

void __init kmem_cache_init(void)
{
	size_t left_over;
	struct cache_sizes *sizes;
	struct cache_names *names;
	int i;
	int order;
	int node;
	/* 在slab初始化好之前,无法通过kmalloc分配初始化过程中必要的一些对象 ,只能使用静态的全局变量 
         ,待slab初始化后期,再使用kmalloc动态分配的对象替换全局变量 */  
  
       /* 如前所述,先借用全局变量initkmem_list3表示的slab三链 
        ,每个内存节点对应一组slab三链。initkmem_list3是个slab三链数组,对于每个内存节点,包含三组 
        :struct kmem_cache的slab三链、struct arraycache_init的slab 三链、struct kmem_list3的slab三链 。这里循环初始化所有内存节点的所有slab三链 */  
	if (num_possible_nodes() == 1) {
		use_alien_caches = 0;
		numa_platform = 0;
	}

	for (i = 0; i < NUM_INIT_LISTS; i++) {//初始化所有node的所有slab中的三个链表
		kmem_list3_init(&initkmem_list3[i]);
		if (i < MAX_NUMNODES)
			cache_cache.nodelists[i] = NULL;//全局静态变量cache_cache,这个变量是用来管理所有缓存的kmem_cache的, 也就是说,在初始化阶段,将会创建一个slab,用来存放所有缓存的kmem_cache
	}

	/*
	 * Fragmentation resistance on low memory - only use bigger
	 * page orders on machines with more than 32MB of memory.
	 */
	if (num_physpages > (32 << 20) >> PAGE_SHIFT)//num_physpages是记录系统实际存在物理内存的总页数,如果大于32M
		slab_break_gfp_order = BREAK_GFP_ORDER_HI;//才可以创建高阶指数内存页数的高速缓存内存对象

	/* Bootstrap is tricky, because several objects are allocated
	 * from caches that do not exist yet:
	 * 1) initialize the cache_cache cache: it contains the struct
	 *    kmem_cache structures of all caches, except cache_cache itself:
	 *    cache_cache is statically allocated.
	 *    Initially an __init data area is used for the head array and the
	 *    kmem_list3 structures, it's replaced with a kmalloc allocated
	 *    array at the end of the bootstrap.
	 * 2) Create the first kmalloc cache.
	 *    The struct kmem_cache for the new cache is allocated normally.
	 *    An __init data area is used for the head array.
	 * 3) Create the remaining kmalloc caches, with minimally sized
	 *    head arrays.
	 * 4) Replace the __init data head arrays for cache_cache and the first
	 *    kmalloc cache with kmalloc allocated arrays.
	 * 5) Replace the __init data for kmem_list3 for cache_cache and
	 *    the other cache's with kmalloc allocated memory.
	 * 6) Resize the head arrays of the kmalloc caches to their final sizes.
	 */

	node = numa_node_id();//获取当前的内存结点号

	//第一步,创建struct kmem_cache所在的cache,由全局变量cache_cache指向,这里只是初始化数据结构,并未真正创建这些对象,要待分配时才创建。全局变量cache_chain是内核slab cache链表的表头 
	INIT_LIST_HEAD(&cache_chain);//初始化保存所有slab cache的全局链表cache_chain
	list_add(&cache_cache.next, &cache_chain);//将cache_cache加入到slab cache链表
	cache_cache.colour_off = cache_line_size();//设置cache着色基本单位为cache line的大小:32字节
	cache_cache.array[smp_processor_id()] = &initarray_cache.cache;//初始化cache_cache的local cache,同样这里也不能使用kmalloc,需要使用静态分配的全局变量initarray_cache
	cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE];//初始化slab链表 ,用全局变量

	/*
	 * struct kmem_cache size depends on nr_node_ids, which
	 * can be less than MAX_NUMNODES.
	 */
	cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
				 nr_node_ids * sizeof(struct kmem_list3 *);//buffer_size保存slab中对象的大小,这里是计算struct kmem_cache的大小,nodelists是最后一个成员,nr_node_ids保存内存节点个数,UMA为1,所以nodelists偏移加上1个struct kmem_list3 的大小即为struct kmem_cache的大小
#if DEBUG
	cache_cache.obj_size = cache_cache.buffer_size;
#endif
	cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
					cache_line_size());//将对象大小与cache line大小对齐
	cache_cache.reciprocal_buffer_size =
		reciprocal_value(cache_cache.buffer_size);//计算对象大小的倒数,用于计算对象在slab中的索引

	for (order = 0; order < MAX_ORDER; order++) {
		cache_estimate(order, cache_cache.buffer_size,
			cache_line_size(), 0, &left_over, &cache_cache.num);//计算cache_cache中的对象数目
		if (cache_cache.num)//num不为0意味着创建struct kmem_cache对象成功,退出
			break;
	}
	BUG_ON(!cache_cache.num);
	cache_cache.gfporder = order;//gfporder表示本slab包含2^gfporder个页面
	cache_cache.colour = left_over / cache_cache.colour_off;//着色区的大小,以colour_off为单位
	cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
				      sizeof(struct slab), cache_line_size());//slab管理对象的大小

	/* 2+3) create the kmalloc caches */
	sizes = malloc_sizes;//malloc_sizes保存大小
	names = cache_names;//cache_names保存cache名

	/*
	 * Initialize the caches that provide memory for the array cache and the
	 * kmem_list3 structures first.  Without this, further allocations will
	 * bug.
	 */
	//首先创建struct array_cache和struct kmem_list3所用的general cache,它们是后续初始化动作的基础
	sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name,
					sizes[INDEX_AC].cs_size,
					ARCH_KMALLOC_MINALIGN,
					ARCH_KMALLOC_FLAGS|SLAB_PANIC,
					NULL);//INDEX_AC是计算local cache所用的struct arraycache_init对象在kmalloc size中的索引,即属于哪一级别大小的general cache,创建此大小级别的cache为local cache所用

	if (INDEX_AC != INDEX_L3) {//如果struct kmem_list3和struct arraycache_init对应的kmalloc size索引不同,即大小属于不同的级别,则创建struct kmem_list3所用的cache,否则共用一个cache
		sizes[INDEX_L3].cs_cachep =
			kmem_cache_create(names[INDEX_L3].name,
				sizes[INDEX_L3].cs_size,
				ARCH_KMALLOC_MINALIGN,
				ARCH_KMALLOC_FLAGS|SLAB_PANIC,
				NULL);
	}

	slab_early_init = 0;//创建完上述两个general cache后,slab early init阶段结束,在此之前,不允许创建外置式slab

	while (sizes->cs_size != ULONG_MAX) {//循环创建kmalloc各级别的general cache
		/*
		 * For performance, all the general caches are L1 aligned.
		 * This should be particularly beneficial on SMP boxes, as it
		 * eliminates "false sharing".
		 * Note for systems short on memory removing the alignment will
		 * allow tighter packing of the smaller caches.
		 */
		if (!sizes->cs_cachep) {//某级别的kmalloc cache还未创建,创建之,struct kmem_list3和struct arraycache_init对应的cache已经创建过了
			sizes->cs_cachep = kmem_cache_create(names->name,
					sizes->cs_size,
					ARCH_KMALLOC_MINALIGN,
					ARCH_KMALLOC_FLAGS|SLAB_PANIC,
					NULL);
		}
#ifdef CONFIG_ZONE_DMA
		sizes->cs_dmacachep = kmem_cache_create(
					names->name_dma,
					sizes->cs_size,
					ARCH_KMALLOC_MINALIGN,
					ARCH_KMALLOC_FLAGS|SLAB_CACHE_DMA|
						SLAB_PANIC,
					NULL);
#endif
		sizes++;
		names++;
	}//至此,kmalloc general cache已经创建完毕,可以拿来使用了
	/* 4) Replace the bootstrap head arrays */
	{//第四步,用kmalloc对象替换静态分配的全局变量。到目前为止一共使用了两个全局local cache,一个是cache_cache的local cache指向initarray_cache.cache,另一个是malloc_sizes[INDEX_AC].cs_cachep的local cache指向initarray_generic.cache,参见setup_cpu_cache函数。这里替换它们。
		struct array_cache *ptr;

		ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);//申请cache_cache所用local cache的空间

		local_irq_disable();
		BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);
		memcpy(ptr, cpu_cache_get(&cache_cache),
		       sizeof(struct arraycache_init));//复制原cache_cache的local cache,即initarray_cache,到新的位置
		/*
		 * Do not assume that spinlocks can be initialized via memcpy:
		 */
		spin_lock_init(&ptr->lock);

		cache_cache.array[smp_processor_id()] = ptr;//cache_cache的local cache指向新的位置
		local_irq_enable();

		ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);//申请malloc_sizes[INDEX_AC].cs_cachep所用local cache的空间

		local_irq_disable();
		BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
		       != &initarray_generic.cache);
		memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
		       sizeof(struct arraycache_init));//复制原local cache到新分配的位置,注意此时local cache的大小是固定的
		/*
		 * Do not assume that spinlocks can be initialized via memcpy:
		 */
		spin_lock_init(&ptr->lock);

		malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
		    ptr;
		local_irq_enable();
	}
	/* 5) Replace the bootstrap kmem_list3's */
	{//第五步,与第四步类似,用kmalloc的空间替换静态分配的slab三链
		int nid;

		/* Replace the static kmem_list3 structures for the boot cpu */
		init_list(&cache_cache, &initkmem_list3[CACHE_CACHE], node);//复制struct kmem_cache的slab三链

		for_each_online_node(nid) {
			init_list(malloc_sizes[INDEX_AC].cs_cachep,
				  &initkmem_list3[SIZE_AC + nid], nid);//复制struct arraycache_init的slab三链

			if (INDEX_AC != INDEX_L3) {
				init_list(malloc_sizes[INDEX_L3].cs_cachep,
					  &initkmem_list3[SIZE_L3 + nid], nid);//复制struct kmem_list3的slab三链
			}
		}
	}

	/* 6) resize the head arrays to their final sizes */
	{//初始化阶段local cache的大小是固定的,要根据对象大小重新计算
		struct kmem_cache *cachep;
		mutex_lock(&cache_chain_mutex);
		list_for_each_entry(cachep, &cache_chain, next)
			if (enable_cpucache(cachep))
				BUG();
		mutex_unlock(&cache_chain_mutex);
	}

	/* Annotate slab for lockdep -- annotate the malloc caches */
	init_lock_keys();


	/* Done! */
	g_cpucache_up = FULL;//大功告成,general cache终于全部建立起来了

	/*
	 * Register a cpu startup notifier callback that initializes
	 * cpu_cache_get for all new cpus
	 */
	register_cpu_notifier(&cpucache_notifier);//注册cpu up回调函数,cpu up时配置local cache

	/*
	 * The reap timers are started later, with a module init call: That part
	 * of the kernel is not yet operational.
	 */
}




分享到:
评论

相关推荐

    论文研究-Linux系统中SLAB分配器内存回收算法分析和优化 .pdf

    Linux系统中SLAB分配器内存回收算法分析和优化,陈卉,张振华,SLAB分配器在linux系统中的使用大大提高了内核中分配内存对象的效率,并且解决了小对象分配内存时产生的内存碎片问题。而该分配器的

    Linux内存管理Slab分配器

    Linux所使用的slab分配器的基础是JeffBonwick 为SunOS操作系统首次引入的一种算法。Jeff的分配器是围绕对象缓存进行的。在内核中,会为有限的对象集(例如文件描述符和其他常见结构)分配大量内存。Jeff 发现对内核...

    Linux Slab分配器分析文档

    Linux下Slab分配器比较详细的分析文档,Slab分配器的思想对程序设计时的内存分配是很有帮组的,3年前写的了,分享给大家,也欢迎指正错误。

    linux slab分配器分析

    本文将着重介绍 Linux 内核的内存管理,尤其是 slab 分配提供的机制。

    linux内核 slab内存分配器分析

    详细介绍了slab的源代码 和原理 对掌握内核内存管理很大帮助 支持2.6版本的

    Linux内存管理中的Slab分配机制.pdf

    Linux内存管理中的Slab分配机制.pdf

    LINUX中的Slab分配器.pdf

    LINUX中的Slab分配器.pdf

    Linux_slab_分配器剖析.pdf

    Linux_slab_分配器剖析.pdf

    Linux内存管理详解.ppt

    详细介绍linux内核内存管理,从内核思想详细介绍伙伴算法、slab内存管理

    Linux2.6+slab内核缓冲区管理

    Linux2.6+slab内核缓冲区管理,个人认为图解讲的还是比较清晰的。有助于理解工作机制。

    linux的内存管理-总结文档

    linux的内存管理分为四个大部分: 1、初始化过程中内存的建立及到伙伴系统的转移; 2、伙伴系统、slab分配器、非连续内存的管理; 3、进程地址空间的内存管理; 4、内存回收;

    Linux内核Slab内存缓冲区管理器.pdf

    Linux内核Slab内存缓冲区管理器.pdf

    linux内存管理-FAQs.pdf

    与内存管理系统相关的各种flags汇总如下:  每个物理page有自己的flags, 定义在struct page -&gt; unsigned long flags; 详情见2.2.2节的《page》  每个内存块(多个page组成一个内存块)有自己的pageblock_flags, ...

    Linux2.6 slab内核缓冲区管理

    Linux2.6 slab内核缓冲区管理 讲的非常不错,对Linux内核的理解非常有帮助

    疯狂内核之——内核初始化

    5.8.2 初始化slab分配器 241 5.8.3 初始化非连续内存区 250 5.9 初始化调度程序 251 5.10 初始化中断处理系统 256 5.10.1 设置APIC中断服务 256 5.10.2 初始化本地软时钟 264 5.10.3 软中断初始化 268 5.10.4 初始化...

    LINUX内存管理

    Linux 提供了对 4KB 缓冲区的抽象,例如 slab 分配器。这种内存管理模式使用 4KB 缓冲区为基数,然后从中分配结构,并跟踪内存页使用情况,比如哪些内存页是满的,哪些页面没有完全使用,哪些页面为空。这样就允许该...

    nginx slab内存管理精简源码及注释

    nginx的slab管理与linux的slab管理相同的地方在于均是利用了内存 的缓存与对齐机制,slab内存管理中一些设计相当巧妙的地方,也有一些地方个人感觉设计 不是很完美,或许是作为nginx设计综合考虑的结果。 nginx slab...

    Memcached内存分配与SLAB机制

    Memcached内存分配与SLAB机制,详细解读

Global site tag (gtag.js) - Google Analytics