slab原始碼分析--從slab初始化說起

FreeeLinux發表於2017-01-14

上次說了 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<<order)
    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 <linux/kmalloc_sizes.h>   //終於明白這是什麼用法了
    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 欄位描述的用來分配的物件大小是不一樣的,以後不同大小的物件就靠這個找不同的快取器分配就行了。


參考:


PS: 最近是怎麼了,有點慌,覺得學了幾天才學了這麼一點東西,感覺好沒有成就感。嗯,沒慌,搞技術的要靜的下心來。

相關文章