Chinaunix首页 | 论坛 | 博客
  • 博客访问: 381186
  • 博文数量: 87
  • 博客积分: 983
  • 博客等级: 准尉
  • 技术积分: 685
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-25 07:20
文章分类

全部博文(87)

文章存档

2016年(1)

2015年(3)

2014年(55)

2013年(13)

2012年(15)

分类: LINUX

2014-07-11 16:00:52

 

本节介绍slub分配器的初始化。

kmem_cache_init

    初始化slub分配器。

void __init kmem_cache_init(void)

{

       int i;

       int caches = 0;

       struct kmem_cache *temp_kmem_cache;

       int order;

       struct kmem_cache *temp_kmem_cache_node;

       unsigned long kmalloc_size;

/* 计算struct kmem_cache对象大小,数据结构struct kmem_cache_node的作用与slab分配器的三链类似,只是做了封装,里面只有双链,去掉了空链 */

       kmem_size = offsetof(struct kmem_cache, node) +

                            nr_node_ids * sizeof(struct kmem_cache_node *);

 

       /* Allocate two kmem_caches from the page allocator */

       /* cache line大小对齐 */

       kmalloc_size = ALIGN(kmem_size, cache_line_size());

/* 首先通过页分配器分配两个struct kmem_cache临时对象,这里计算它们所占页块的order。此时struct kmem_cache本身的cache还没有建立起来,所以不能使用kmem_cache_alloc */

       order = get_order(2 * kmalloc_size);

/* 全局变量kmem_cache指向第一个struct kmem_cache临时对象 */

       kmem_cache = (void *)__get_free_pages(GFP_NOWAIT, order);

 

       /*

        * Must first have the slab cache available for the allocations of the

        * struct kmem_cache_node's. There is special bootstrap code in

        * kmem_cache_open for slab_state == DOWN.

        */

       /* 全局变量kmem_cache_node指向第二个struct kmem_cache临时对象 */

       kmem_cache_node = (void *)kmem_cache + kmalloc_size;

/* 初始化这个struct kmem_cache临时对象,这个cache用于保存struct kmem_cache_node对象 */

       kmem_cache_open(kmem_cache_node, "kmem_cache_node",

              sizeof(struct kmem_cache_node),

              0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

       /* 内存热插拔相关 */

       hotplug_memory_notifier(slab_memory_callback, SLAB_CALLBACK_PRI);

 

       /* Able to allocate the per node structures */

/* 修改slab初始化进度。建立好struct kmem_cache_node对象所在cache之后,即可使用它来分配struct kmem_cache_node对象了,不用再通过__get_free_pages分配了 */

       slab_state = PARTIAL;

       /* 指向第一个struct kmem_cache临时对象 */

       temp_kmem_cache = kmem_cache;

/* 初始化这个struct kmem_cache临时对象,这个cache用于保存struct kmem_cache对象 */

       kmem_cache_open(kmem_cache, "kmem_cache", kmem_size,

              0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

/* 上面两个cache建立好后,就可以通过kmem_cache_alloc从里面分配对象,从而替换前面通过页分配器分配的临时对象。这一步骤与slab分配器类似,只不过slab分配器是静态分配的全局变量,slub分配器是__get_free_pages分配的。*/

/* 全局变量kmem_cache指向新分配的struct kmem_cache对象 */

       kmem_cache = kmem_cache_alloc(kmem_cache, GFP_NOWAIT);

       /* 对象复制 */

       memcpy(kmem_cache, temp_kmem_cache, kmem_size);

 

       /*

        * Allocate kmem_cache_node properly from the kmem_cache slab.

        * kmem_cache_node is separately allocated so no need to

        * update any list pointers.

        */

       /* 指向第二个struct kmem_cache临时对象 */

       temp_kmem_cache_node = kmem_cache_node;

/* 全局变量kmem_cache_node指向新分配的struct kmem_cache对象 */

       kmem_cache_node = kmem_cache_alloc(kmem_cache, GFP_NOWAIT);

       /* 对象复制 */

       memcpy(kmem_cache_node, temp_kmem_cache_node, kmem_size);

/* 初始化阶段,配置cache中的slab,使每个slab首页面的slab指针指向所在的cache节点 /

       kmem_cache_bootstrap_fixup(kmem_cache_node);

       /* cache数加一 */

       caches++;     

       kmem_cache_bootstrap_fixup(kmem_cache);

       caches++;

       /* Free temporary boot structure */

       /* 释放初始化阶段使用的临时页面 */

       free_pages((unsigned long)temp_kmem_cache, order);

 

       /* Now we can use the kmem_cache to allocate kmalloc slabs */

/* 至此,slub分配器正常运转需要的struct kmem_cache对象和struct kmem_cache_node对象的cache节点都已经初始化完毕,可以通过它们继续创建kmalloc所用的cache */

       /*

        * Patch up the size_index table if we have strange large alignment

        * requirements for the kmalloc array. This is only the case for

        * MIPS it seems. The standard arches will not generate any code here.

        *

        * Largest permitted alignment is 256 bytes due to the way we

        * handle the index determination for the smaller caches.

        *

        * Make sure that nothing crazy happens if someone starts tinkering

        * around with ARCH_KMALLOC_MINALIGN

        */

       /*    有必要先了解一下几个重要的宏:

1)        ARCH_DMA_MINALIGN:体系结构定义的DMA操作时的最小对齐值。通常是针对MIPS而言,可能的取值为:32字节、128字节等。

2)        ARCH_KMALLOC_MINALIGN:如果定义了ARCH_DMA_MINALIGN,比如MIPS,那么取值就是ARCH_DMA_MINALIGN,否则取值为默认的8字节。这个宏定义了kmalloc cache中对象的最小对齐方式,比如取值128字节,那么kmalloc cache中所有对象都必须是128字节对齐的。Slub模型支持的这个宏的最大值为256字节。

3)        KMALLOC_MIN_SIZE:如果定义了ARCH_DMA_MINALIGN,并且ARCH_DMA_MINALIGN大于8,比如MIPS,那么取值就是ARCH_DMA_MINALIGN,否则取值为默认的8字节。定义了kmalloc cache中对象的最小大小。比如取值为128字节,那么kmalloc cache中的最小对象大小是128字节,当申请一个60字节的对象时,需要到128字节的kmalloc cache中分配。

4)        KMALLOC_SHIFT_LOW:根据KMALLOC_MIN_SIZE算出的最小对象在kmalloc_caches数组中的索引。

 

    然后需要了解一下kmalloc的原理,如图所示:

    我们以通常情况下KMALLOC_MIN_SIZE等于8为例进行说明。size_index[0-23]数组根据对象大小映射到不同的kmalloc_caches[0-13]保存的cache。观察kmalloc_caches[0-13]数组,可见索引即该cache slab块的order

    由于最小对象为823)字节,kmalloc_caches[0-2]这三个数组元素没有用到,slub使用kmalloc_caches[1]保存96字节大小的对象,kmalloc_caches[2] 保存192字节大小的对象,相当于细分了kmalloc的粒度,有利于减少空间的浪费。kmalloc_caches[0]未使用。

    当申请一个对象时,首先确定该对象所属的kmalloc区间,即order。如果没有96192这两个特殊的大小,那么就没有必要引入size_index[0-23]数组了,可直接通过对象大小得到order。由于这两个特殊大小kmalloc cache的存在,我们需要通过size_index[0-23]数组将某些大小的对象对应到kmalloc_caches[1-2]去。size_index[0-23]数组按照最小的对象大小KMALLOC_MIN_SIZE递增,如上图所示,72-96大小的对象都被对应到kmalloc_caches[1]136-192大小的对象都被对应到kmalloc_caches[2]。大于192的无需特别计算,直接计算order即可。

*/

       /* KMALLOC_MIN_SIZE进行检查,不得大于256,必须是2的幂数 */

       BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 ||

              (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1)));

/* 下面这些代码仅针对KMALLOC_MIN_SIZE大于8的情况,对size_index数组进行调整 */

/* 如果KMALLOC_MIN_SIZE大于8size_index用于根据对象的大小,计算其对应的kmalloc_caches索引 */

       for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) {

              /* 根据对象大小计算其在size_index数组中的索引 */

              int elem = size_index_elem(i);

              /* 索引超出size_index数组的范围 */

              if (elem >= ARRAY_SIZE(size_index))

                     break;

/* 这些size_index数组的值都指向最小的kmalloc order。比如KMALLOC_MIN_SIZE128,那么所有小于128size_index都对应到27 kmalloc cache*/

              size_index[elem] = KMALLOC_SHIFT_LOW;

       }

 

       if (KMALLOC_MIN_SIZE == 64) {

              /*

               * The 96 byte size cache is not used if the alignment

               * is 64 byte.

               */

/* KMALLOC_MIN_SIZE64时,说明对象的最小对齐大小为6496大小的kmalloc cache无法满足要求,将原本映射到该cache的对象映射到12827kmalloc cache */

              for (i = 64 + 8; i <= 96; i += 8)

                     size_index[size_index_elem(i)] = 7;

       } else if (KMALLOC_MIN_SIZE == 128) {

              /*

               * The 192 byte sized cache is not used if the alignment

               * is 128 byte. Redirect kmalloc to use the 256 byte cache

               * instead.

               */

/* 原理同上,将原本映射到192 kmalloc cache的对象映射到12827kmalloc cache */

              for (i = 128 + 8; i <= 192; i += 8)

                     size_index[size_index_elem(i)] = 8;

       }

 

       /* Caches that are not of the two-to-the-power-of size */

/* KMALLOC_MIN_SIZE小于等于32时,才创建96 kmalloc cache,大于3296不满足对象对齐要求,无需创建 */

       if (KMALLOC_MIN_SIZE <= 32) {

              kmalloc_caches[1] = create_kmalloc_cache("kmalloc-96", 96, 0);

              caches++;

       }

/* KMALLOC_MIN_SIZE小于等于64时,才创建192 kmalloc cache,大于64192不满足对象对齐要求,无需创建 */

       if (KMALLOC_MIN_SIZE <= 64) {

              kmalloc_caches[2] = create_kmalloc_cache("kmalloc-192", 192, 0);

              caches++;

       }

/* 这里创建kmalloc大小为2324…213 所对应的cache,分别保存在kmalloc_caches[3-13]中,注意这些cache名字相同,后面会重命名。当KMALLOC_MIN_SIZE大于8时,比如64kmalloc_caches[3-5]未使用 */

       for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) {

              kmalloc_caches[i] = create_kmalloc_cache("kmalloc", 1 << i, 0);

              caches++;

       }

       /* 修改slub分配器的初始化状态为UP,表示kmalloc可用了 */

       slab_state = UP;

 

       /* Provide the correct kmalloc names now that the caches are up */

/* 前面创建kmalloc cache时,其name指针都指向一个常量字符串,需要为该指针分配一个存储空间 */

       if (KMALLOC_MIN_SIZE <= 32) {

              kmalloc_caches[1]->name = kstrdup(kmalloc_caches[1]->name, GFP_NOWAIT);

              BUG_ON(!kmalloc_caches[1]->name);

       }

 

       if (KMALLOC_MIN_SIZE <= 64) {

              kmalloc_caches[2]->name = kstrdup(kmalloc_caches[2]->name, GFP_NOWAIT);

              BUG_ON(!kmalloc_caches[2]->name);

       }

       /* 这里不只分配存储空间,还按照大小重新命名 */

       for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) {

              char *s = kasprintf(GFP_NOWAIT, "kmalloc-%d", 1 << i);

 

              BUG_ON(!s);

              kmalloc_caches[i]->name = s;

       }

 

#ifdef CONFIG_SMP

       /* 添加一个CPU通知链节点,当CPU下线时清除该CPUlocal slab */

       register_cpu_notifier(&slab_notifier);

#endif

 

#ifdef CONFIG_ZONE_DMA

       /* 如果支持DMA,那么创建一组与kmalloc_caches相同的cacheDMA使用 */

       for (i = 0; i < SLUB_PAGE_SHIFT; i++) {

              struct kmem_cache *s = kmalloc_caches[i];

 

              if (s && s->size) {

                     char *name = kasprintf(GFP_NOWAIT,

                             "dma-kmalloc-%d", s->objsize);

 

                     BUG_ON(!name);

                     kmalloc_dma_caches[i] = create_kmalloc_cache(name,

                            s->objsize, SLAB_CACHE_DMA);

              }

       }

#endif

       printk(KERN_INFO

              "SLUB: Genslabs=%d, HWalign=%d, Order=%d-%d, MinObjects=%d,"

              " CPUs=%d, Nodes=%d\n",

              caches, cache_line_size(),

              slub_min_order, slub_max_order, slub_min_objects,

              nr_cpu_ids, nr_node_ids);

}

 

kmem_cache_bootstrap_fixup

    初始化阶段,配置cache中的slab

static void __init kmem_cache_bootstrap_fixup(struct kmem_cache *s)

{

       int node;

       /* cache加入到全局cache链(由全局变量slab_caches保存) */

       list_add(&s->list, &slab_caches);

/* slub模型初始化阶段创建的struct kmem_cache_node cachestruct kmem_cache cache不允许复用,设置refcount-1,参见slab_unmergeable。而其他cache创建后,refcount1,参见kmem_cache_open */

       s->refcount = -1;

       /* 遍历每个内存节点 */

       for_each_node_state(node, N_NORMAL_MEMORY) {

              /* 获得本节点的slab */

              struct kmem_cache_node *n = get_node(s, node);

              struct page *p;

 

              if (n) {

                     /* 遍历部分满slab链,设置每个slab指向cache的指针 */

                     list_for_each_entry(p, &n->partial, lru)

                            p->slab = s;

 

#ifdef CONFIG_SLAB_DEBUG

/* 遍历满slab链,设置每个slab指向cache的指针 */

                     list_for_each_entry(p, &n->full, lru)

                            p->slab = s;

#endif

              }

       }

}

 

create_kmalloc_cache

    创建kmalloc使用的cachekmem_cache_create是基于kmalloc的,所以slub初始化阶段不能使用kmem_cache_create。这点与slab模型不同,slab模型的kmalloc cache也是通过kmem_cache_create创建的。

static struct kmem_cache *__init create_kmalloc_cache(const char *name,

                                          int size, unsigned int flags)

{

       struct kmem_cache *s;

       /* 分配一个cache对象 */

       s = kmem_cache_alloc(kmem_cache, GFP_NOWAIT);

 

       /*

        * This function is called with IRQs disabled during early-boot on

        * single CPU so there's no need to take slub_lock here.

        */

/* 初始化这个cache */

       if (!kmem_cache_open(s, name, size, ARCH_KMALLOC_MINALIGN,

                                                        flags, NULL))

              goto panic;

       /* 加入到全局cache链中 */

       list_add(&s->list, &slab_caches);

       return s;

 

panic:

       panic("Creation of kmalloc slab %s size=%d failed.\n", name, size);

       return NULL;

}

 

    至此,Linux slub分配器已经介绍完毕,怎么样,是不是体会到了其相对于slab模型的优越之处。

 

 

 

阅读(945) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~