slab源码分析--从slab初始化说起

上次说了 slab 的主要数据结构,这次从初始化开始进行源码剖析。

slab 的初始化,当然是从内核启动就开始了。内核启动的 start_kernel() 函数:

//内核的启动程序啊:)
asmlinkage void __init start_kernel(void)
{
    ...
    mem_init();    //内存相关初始化
    kmem_cache_init();    //slab 初始化
    ...
}

其中调用 kmem_cache_init() 函数,这就是要进行 slab 的初始化。

/*
 * Initialisation.  Called after the page allocator have been initialised and
 * before smp_init().
 */
 //这个函数用来建立通用缓存器,否则我们无法使用kmalloc
void __init kmem_cache_init(void)
{
    size_t left_over;
    struct cache_sizes *sizes;
    struct cache_names *names;
    int i;
    int order;
    int node;

    //如果节点数目只有1个,那么就不能使用其他节点的shared cache
    if (num_possible_nodes() == 1)  //貌似当前这个版本还不支持这个,参见#define num_possible_nodes() 1
        use_alien_caches = 0;             //后续linux版本会支持

    //在slab初始化好之前,无法通过kmalloc分配初始化过程中的一些必要对象,只能使用静态的全局变量
    //待slab初始化后期,再使用kmalloc动态分配对象替换全局变量!!

    //如前所述,先借用 initkem_list3 代替要用到的三链,每个节点对应一组三链
    //initkmem_list3 是个三链数组,这里循环初始化每个节点的三链 
    for (i = 0; i < NUM_INIT_LISTS; i++) {       //FIXME: NUM_INIT_LIST 是怎么求的,为什么等于2 * MAX_NODES+1 ? 有的版本是 3 * MAX_NODES
        kmem_list3_init(&initkmem_list3[i]);
        if (i < MAX_NUMNODES)
        //全局变量cache_cache指向的slab cache包含所有的kmem_cache对象,不包含cache_cache本身
        //这里初始化所有内存节点kmem_cache的slab三链为空
            cache_cache.nodelists[i] = NULL;
    }

    /*
     * Fragmentation resistance on low memory - only use bigger
     * page orders on machines with more than 32MB of memory.
     */
     //全部变量slab_break_gfp_order为每个slab最多占用几个页面,用来减少碎片。
     //总结起来意思就是是: (1)如果物理可用内存大于32MB,也就是可用内存充裕的时候,BREAK_GFP_ORDER_HI这个宏的值是1,
     //这个时候每个slab最多占用两个页面,不过此时不能横跨3个页面,除非对象大小大于8192K时才可以(一个页面大小是4K,也就是4096);
     //(2)如果可用内存不大于32MB,那么BREAK_GFP_ORDER_HI值为0,最多也就是允许一个页面,除非对象超了,否则不能横跨
    if (num_physpages > (32 << 20) >> PAGE_SHIFT)
        slab_break_gfp_order = BREAK_GFP_ORDER_HI;   //用来确定slab的最大大小

    /* 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();   //获取节点id,取得的值为0,因为初始化程序单CPU执行,且是0号

    /* 1) create the cache_cache */
    //初始化cache_chain 为 kmem_cache 链表头部
    INIT_LIST_HEAD(&cache_chain);
    list_add(&cache_cache.next, &cache_chain);
    //设置cache着色的偏移量基本值,也就是L1缓存行的大小
    cache_cache.colour_off = cache_line_size(); //宏定义L1缓存行的大小 #define cache_line_size() L1_CACHE_BYTES
    //初始化cache_cache的per-CPU cache,同样这里也不能使用kmalloc,需要使用静态分配的全局变量initarray_cache
    cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
    //初始化slab链表,用全局变量,这里CACHE_CACHE值为0,是因为cache_cache就是系统第一个的缓存器
    cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE];

    /*
     * struct kmem_cache size depends on nr_node_ids, which
     * can be less than MAX_NUMNODES.
     */
     //buffer_size原指用来分配的对象大小,由于cache_cache是用来做 kmem_cache 的分配器的,所以 buffer_size 的大小就是 kmem_cache 的大小
     //注意柔性数组的计算方法,nodelists不占据 kmem_cache的大小,所以要分开计算,并且注意nodelists数组在UMA架构只有一个节点,所以只有1个kmem_list3的指针
    cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
                 nr_node_ids * sizeof(struct kmem_list3 *);
#if 0 
#if DEBUG
    cache_cache.obj_size = cache_cache.buffer_size;
#endif
#endif

    //注意这里又一次计算了 buffer_size 的大小,通过这次计算将buffer_size以 缓存行 为单位进行上边界对齐
    //计算分配的对象与cache line的大小对齐后的大小
    cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
                    cache_line_size());
    //计算对象大小的倒数,用于计算对象在slab中的索引
    cache_cache.reciprocal_buffer_size =
        reciprocal_value(cache_cache.buffer_size);

    //计算cache_cache的剩余空间以及slab中对象的数目,order决定了slab的大小(PAGE_SIZEE<
    for (order = 0; order < MAX_ORDER; order++) { //#define MAX_ORDER 11

        cache_estimate(order, cache_cache.buffer_size,  //buffer_size已经和cache line对齐过
            cache_line_size(), 0, &left_over, &cache_cache.num);  //计算cache_cache的对象数目

        if (cache_cache.num) //num不为0意味着struct kmem_cache对象创建成功,退出,所以在此处就会确定order的大小,以赋值给gfporder
            break;
    }
    BUG_ON(!cache_cache.num);  //断言

    cache_cache.gfporder = order;  //gfporder表示本slab包含2^gfproder个页面,注意这里就从上面的order赋值给 gfporder

    //colour_off 就是上面初始化的 L1_CACHE_BYTES,既然已经知道 L1 缓存行的大小,我们上步又计算出了浪费空间的大小
    //那么用浪费空间的大小 / L1 缓存行的大小,就得出当前可用 colour 的数目,这个数目是累加且循环的,可以为0
    cache_cache.colour = left_over / cache_cache.colour_off;   //确定可用 colour 的数目,单位是 colour_off


    //确定slab描述符以及kmem_bufctl_t数组 针对缓存行对齐后的大小
    cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +  
                      sizeof(struct slab), cache_line_size());


    /* 2+3) create the kmalloc caches */
    sizes = malloc_sizes;   //malloc_sizes数组保存着要分配的大小
    names = cache_names;  //cache_name保存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,它们是后续初始化动作的基础
    //INDEX_AC是计算local cache所用的struct arraycache_init对象在kmalloc size中的索引,即属于哪一级别大小的general cache,创建此大小级别的cache为local 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,   //#define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN,已经对齐过的标记
                    NULL, NULL);

    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, NULL);
    }

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

    //sizes->cs_size 初值为是malloc_sizes[0],值应该是从32开始
    while (sizes->cs_size != ULONG_MAX) {  //循环创建kmalloc各级别的通用缓存器,ULONG_MAX 是最大索引值,
        /*
         * 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) {   
            sizes->cs_cachep = kmem_cache_create(names->name,
                    sizes->cs_size,
                    ARCH_KMALLOC_MINALIGN,
                    ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                    NULL, NULL);
        }
#ifdef CONFIG_ZONE_DMA   //如果配置DMA,那么为每个kmem_cache 分配两个,一个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, NULL);
#endif
        sizes++;   //都是数组名,直接++,进行循环迭代,由小到大分配各个大小的general caches,最大为ULONG_MAX
        names++;
    }
    /* 4) Replace the bootstrap head arrays */
    {
        struct array_cache *ptr;

        //现在要申请arraycache替换之前的initarray_cache
        ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);  //GFP_KERNEL 可睡眠申请

        //关中断
        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中per-cpu对应的array_cache拷贝到ptr
        /*
         * Do not assume that spinlocks can be initialized via memcpy:
         */
        spin_lock_init(&ptr->lock);

        cache_cache.array[smp_processor_id()] = ptr;  //再让它指向ptr?
        local_irq_enable();

        ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);

        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));
        /*
         * 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 */
    {
        int nid;

        /* Replace the static kmem_list3 structures for the boot cpu */
        init_list(&cache_cache, &initkmem_list3[CACHE_CACHE], node);

        for_each_online_node(nid) {
            init_list(malloc_sizes[INDEX_AC].cs_cachep,
                  &initkmem_list3[SIZE_AC + nid], nid);

            if (INDEX_AC != INDEX_L3) {
                init_list(malloc_sizes[INDEX_L3].cs_cachep,
                      &initkmem_list3[SIZE_L3 + nid], nid);
            }
        }
    }

    /* 6) resize the head arrays to their final sizes */
    {
        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;

    /*
     * Register a cpu startup notifier callback that initializes
     * cpu_cache_get for all new cpus
     */
    register_cpu_notifier(&cpucache_notifier);

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

这个函数就是 slab 初始化的主干。
执行流程:

  1. 使用 initkmem_list3 和 intarray_cache 两个静态量,再加上手动填充cache_cache 的其他变量 ,完成对 cache_cache 的初始化。这是用来缓存 kmem_cache 的,相当于是缓存器的缓存器:)
  2. 创建 kmalloc 所用的其他通用缓存器:
    1. cache 的名称和大小放在 names[] 和 malloc_sizes[] 两个数组中,对应大小的 cache 可以从 malloc_sizes[] 中找到,索引就是要分配对象的大小。
    2. 先创建 INDEX_AC 和 INDEX_L3 下标的 cache(array_cache 和 三链,如果大小一样,只创建一次)。
    3. 循环创建 size 数组中各个大小的 cache。
  3. 替换静态本地 cache 全局变量:
    1. 替换 malloc_sizes[INDEX_AC].cs_cachep 的 local cache,原本指向静态变量 initarray_cache.cache。
    2. 替换 malloc_sizes[INDEX_AC].cx_cachep 的 local cache,原本指向静态变量 initarray_generic.cache。 ( kmem_cache_create() 中调用 setup_cpu_cache() 函数会使用该静态变量)。
  4. 替换静态三链:
    1. 替换 cache_cache 三链,原本指向静态变量的 initkmem_list3。
    2. 替换 malloc_sizes[INDEX_AC].cs_cachep 三链,原本指向静态变量 initkmem_list3。
  5. 更新初始化进度


静态初始化的结构比如:

/* internal cache of cache description objs */
//这就是静态定义了第一个即通用缓存器
static struct kmem_cache cache_cache = {
    .batchcount = 1,
    .limit = BOOT_CPUCACHE_ENTRIES,
    .shared = 1,
    .buffer_size = sizeof(struct kmem_cache),
    .name = "kmem_cache",  //卧槽,名字就叫 kmem_cache
};

或者

static struct arraycache_init initarray_cache /*__initdata*/ =
    { {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
static struct arraycache_init initarray_generic =
    { {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };


该函数其他辅助函数有:
1. 三链初始化函数

static void kmem_list3_init(struct kmem_list3 *parent)
{
    INIT_LIST_HEAD(&parent->slabs_full);
    INIT_LIST_HEAD(&parent->slabs_partial);
    INIT_LIST_HEAD(&parent->slabs_free);
    parent->shared = NULL;
    parent->alien = NULL;
    parent->colour_next = 0;
    spin_lock_init(&parent->list_lock);
    parent->free_objects = 0;
    parent->free_touched = 0;
}

这个没什么卵用,就只是初始化。

2. 对齐宏

#define ALIGN(x,a)      __ALIGN_MASK(x,(typeof(x))(a)-1)
#define __ALIGN_MASK(x,mask)    (((x)+(mask))&~(mask))

这个宏用来控制内存以 a 的大小为基本单位对齐,0~7为一组,以此类推。

关于这个宏详细解释可参照:内核宏ALIGN的含义

3. cache_estimate()函数
这个函数用来计算每个 slab 中对象的数目以及浪费空间的大小,注释中的管理对象就是前面所说的管理者。

/*
 * Calculate the number of objects and left-over bytes for a given buffer size.
 */
static void cache_estimate(unsigned long gfporder, size_t buffer_size,
               size_t align, int flags, size_t *left_over,
               unsigned int *num)
{
    int nr_objs;
    size_t mgmt_size;

    //slab 大小为 2^gfporder 个页面
    size_t slab_size = PAGE_SIZE << gfporder;  //#define PAGE_SIZE 0x400 (即1024),FIXME: 难道一个页面是 1K ,怎么可能?

    /*
     * The slab management structure can be either off the slab or  //有off-slab和on-slab两种方式
     * on it. For the latter case, the memory allocated for a
     * slab is used for:   //这段内存被用来存储:
     *
     * - The struct slab      //slab结构体
     * - One kmem_bufctl_t for each object    //每个对象的kmem_bufctl_t
     * - Padding to respect alignment of @align  //对齐的大小
     * - @buffer_size bytes for each object   //每个对象的大小
     *
     * If the slab management structure is off the slab, then the
     * alignment will already be calculated into the size. Because   //如果是off-slab,align早已被计算出来
     * the slabs are all pages aligned, the objects will be at the   //因为所有的页面对齐过了,对象申请时会处在正确的位置
     * correct alignment when allocated.
     */
     //对于外置slab,没有slab管理对象问题,直接用申请空间除以对象大小就是对象个数
    if (flags & CFLGS_OFF_SLAB) {
        //外置slab不存在管理对象,全部用于存储slab对象
        mgmt_size = 0;
        //所以对象个数 = slab大小 / 对象大小
        nr_objs = slab_size / buffer_size;    //注意buffer_size已经和cache line对齐过了

        //对象个数不许超限
        if (nr_objs > SLAB_LIMIT)
            nr_objs = SLAB_LIMIT;
    } else {
        /*
         * Ignore padding for the initial guess. The padding
         * is at most @align-1 bytes, and @buffer_size is at
         * least @align. In the worst case, this result will
         * be one greater than the number of objects that fit
         * into the memory allocation when taking the padding
         * into account.
         */
         //内置式slab,slab管理对象与slab对象都在一片内存中,此时slab页面包含:
         //一个struct slab 对象,一个kmem_bufctl_t 类型数组(kmem_bufctl_t 数组的项数和slab对象数目相同)
        //slab大小需要减去管理对象大小,所以对象个数为 剩余大小 / (每个对象大小 + sizeof(kmem_bufctl_t)), 它们是一一匹配的关系
        nr_objs = (slab_size - sizeof(struct slab)) /
              (buffer_size + sizeof(kmem_bufctl_t));

        /*
         * This calculated number will be either the right
         * amount, or one greater than what we want.
         */
         //如果对齐后超过slab 总大小 ,需要减去一个对象
        if (slab_mgmt_size(nr_objs, align) + nr_objs*buffer_size
               > slab_size)
            nr_objs--;

        //对象个数不许超限
        if (nr_objs > SLAB_LIMIT)
            nr_objs = SLAB_LIMIT;

        //计算  管理对象以缓存行  对齐后的总大小
        mgmt_size = slab_mgmt_size(nr_objs, align);
    }


    //得出slab最终对象个数
    *num = nr_objs;


    //前面已经得到了slab管理对象大小(外置为0,内置也已计算),这样就可以最终的出slab最终浪费空间大小
    *left_over = slab_size - nr_objs*buffer_size - mgmt_size;
}


5. malloc_sizes[]表
我们的需要通过 kmem_cache_create() 函数创建通用缓存器,最终实际会向伙伴系统申请。那创建多大呢?这个有一个很有意思的地方:
首先看 INDEX_AC:

#define INDEX_AC index_of(sizeof(struct arraycache_init))

而 index_of 是这样的:

/*
 * This function must be completely optimized away if a constant is passed to
 * it.  Mostly the same as what is in linux/slab.h except it returns an index.
 */
static __always_inline int index_of(const size_t size) //这个函数用来选择大小的,可作为mallloc_size的参数  
{
    extern void __bad_size(void);

    if (__builtin_constant_p(size)) {
        int i = 0;

#define CACHE(x) \
    if (size <=x) \     //适配一个刚足够容纳size的大小
        return i; \
    else \
        i++;   //不成功,增大继续适配
#include "linux/kmalloc_sizes.h"
#undef CACHE
        __bad_size();
    } else
        __bad_size();
    return 0;
}   

为什么说 index_of 能确定大小呢,这真是有点玄幻了。注意这一句

#include "linux/kmalloc_sizes.h"

而 linux/kmalloc_sizes.h 中的内容是这样的:

#if (PAGE_SIZE == 4096)
    CACHE(32)
#endif
    CACHE(64)
#if L1_CACHE_BYTES < 64
    CACHE(96)
#endif
    CACHE(128)
#if L1_CACHE_BYTES < 128
    CACHE(192)
#endif
    CACHE(256)
    CACHE(512)
    CACHE(1024)
    CACHE(2048)
    CACHE(4096)
    CACHE(8192)
    CACHE(16384)
    CACHE(32768)
    CACHE(65536)
    CACHE(131072)
#if KMALLOC_MAX_SIZE >= 262144
    CACHE(262144)
#endif
#if KMALLOC_MAX_SIZE >= 524288
    CACHE(524288)
#endif
#if KMALLOC_MAX_SIZE >= 1048576
    CACHE(1048576)
#endif
#if KMALLOC_MAX_SIZE >= 2097152
    CACHE(2097152)
#endif
#if KMALLOC_MAX_SIZE >= 4194304
    CACHE(4194304)
#endif
#if KMALLOC_MAX_SIZE >= 8388608
    CACHE(8388608)
#endif
#if KMALLOC_MAX_SIZE >= 16777216
    CACHE(16777216)
#endif
#if KMALLOC_MAX_SIZE >= 33554432
    CACHE(33554432)
#endif

在 index_of 函数中局部定义了 CACHE(x) 宏,并把这个头文件引入,你可以理解为把头文件中所有的内容都加入到了 index_of 函数的那一句所在的位置。那么,程序接下来会不停地执行者个局部宏 CACHE(X),而这个宏又是有函数意义的,当匹配到正确的正确的size,会直接返回 i 值(真是神一样的技巧,相当于用宏来循环,我算是服了)。通过这种方式就确定了要申请的大小对应在 malloc_sizes 表中下标 i 的值。

那么只知道下标 i 的值可不行,malloc_sizes[]表是什么时候初始化的呢? 答案是它是静态初始化的。

/*
 * These are the default caches for kmalloc. Custom caches can have other sizes.
 */
struct cache_sizes malloc_sizes[] = {  //通用缓存器的大小由malloc_size表决定
#define CACHE(x) { .cs_size = (x) },
#include    //终于明白这是什么用法了
    CACHE(ULONG_MAX)
#undef CACHE
};

在上面的代码中,同样定义了 CACHE(X),不过这个可和之前的那个作用不一样,它们俩都是局部宏,使用完即 undef,不会产生影响。

在这里注意,这个宏被定义为 “{ .cs_size = (x) },”,注意到这个逗号了没有?下面会同样引入 kmalloc_size 头文件中的一大堆 CACHE(x),每个都是这种形式,这不正是数组的初始化方式吗?只不过每个数据类型都是 cache_sizes 类型罢了。相当于int array[] = { {1}, {2}, …},大概就是这样。cache_names[] 表同理。

cache_sizes 类型如下:

/* Size description struct for general caches. */
struct cache_sizes {
    size_t          cs_size;    //通用缓存器的大小
    struct kmem_cache   *cs_cachep;   //通用缓存器的描述符指针
#ifdef CONFIG_ZONE_DMA
    struct kmem_cache   *cs_dmacachep;
#endif
};

于是,通过上面这种初始化方式,就把 malloc_sizes[] 表中按照由小到大顺序将全部 cache_sizes的 cs_size 成员初始化了。所以 malloc_size[i] 对应的就是描述大小为 i 的缓存器的 cache_sizes 结构,通过 .cs_cachep 就可以得到对应大小的通用缓存器!

如果是 DMA,那么会配置两种通用缓存器。

6. kmem_cache_create()函数
这个函数是核心,对该函数的详细分析会在下一篇博客出现,很长的:)

7. kmalloc()函数
也是重点,以后会单独剖析。

8. init_list()函数

/*
 * swap the static kmem_list3 with kmalloced memory
 */
static void init_list(struct kmem_cache *cachep, struct kmem_list3 *list,
            int nodeid)
{
    struct kmem_list3 *ptr;

    ptr = kmalloc_node(sizeof(struct kmem_list3), GFP_KERNEL, nodeid);
    BUG_ON(!ptr);

    local_irq_disable();
    memcpy(ptr, list, sizeof(struct kmem_list3));
    /*
     * Do not assume that spinlocks can be initialized via memcpy:
     */
    spin_lock_init(&ptr->list_lock);

    MAKE_ALL_LISTS(cachep, ptr, nodeid);
    cachep->nodelists[nodeid] = ptr;
    local_irq_enable();
}

这是利用 kmalloc 申请三链和静态三链交换的函数,为什么说是利用 kmalloc 呢,且看它调用的 kmalloc_node() 函数:

static inline void *kmalloc_node(size_t size, gfp_t flags, int node)
{
    return kmalloc(size, flags);
}   

就是这样,我们为了解决“鸡与蛋”的问题,首先设定几个静态变量,比如三链 initkmem_list3,先用它合成我们的 cache_cache,然后有了 cache_cache,这是为缓存器制定规则的缓存器,通过它我们就可以随意合成大小其他缓存器了。有了各种大小的缓存器,在 kmem_cache_init() 函数后半部分就可以用缓存器来 kmalloc 分配对象,替换之前所有用来辅助 cache_cache 的静态数据结构(如 initarray_cache, initkmem_list3),以后这些静态数据结构就不再使用了。

cache_cache 为缓存器大小制定了一种规则,这说明所有缓存器自身大小也是一样的,只不过它们的 buffer_size 字段描述的用来分配的对象大小是不一样的,以后不同大小的对象就靠这个找不同的缓存器分配就行了。


参考:

  • kmem_cache_init初始化文字解析
  • Linux Slab分配器(一)–概述


PS: 最近是怎么了,有点慌,觉得学了几天才学了这么一点东西,感觉好没有成就感。嗯,没慌,搞技术的要静的下心来。

你可能感兴趣的:(Linux内核分析)