Linux內存管理之slab機制(初始化)

火星人 @ 2014-03-03 , reply:0
←手機掃碼閱讀

Linux內存管理之slab機制(初始化)

Linux內存管理之slab機制(初始化)


一、內核啟動早期初始化
start_kernel()->mm_init()->kmem_cache_init()

執行流程:

1,初始化靜態initkmem_list3三鏈;

2,初始化cache_cache的nodelists欄位為1中的三鏈;

3,根據內存情況初始化每個slab佔用的頁面數變數slab_break_gfp_order;

4,將cache_cache加入cache_chain鏈表中,初始化cache_cache;

5,創建kmalloc所用的general cache:

1)cache的名稱和大小存放在兩個數據結構對應的數組中,對應大小的cache可以從size數組中找到;

2)先創建INDEX_AC和INDEX_L3下標的cache;

3)循環創建size數組中各個大小的cache;

6,替換靜態本地cache全局變數:

1) 替換cache_cache中的arry_cache,本來指向靜態變數initarray_cache.cache;

2) 替換malloc_sizes.cs_cachep的local cache,原本指向靜態變數initarray_generic.cache;

7,替換靜態三鏈

1)替換cache_cache三鏈,原本指向靜態變數initkmem_list3;

2)替換malloc_sizes.cs_cachep三鏈,原本指向靜態變數initkmem_list3;

8,更新初始化進度

view plaincopy to clipboardprint?/*
* Initialisation.  Called after the page allocator have been initialised and
* before smp_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;  
    /*初始化所有node的所有slab中的三個鏈表*/  
    for (i = 0; i < NUM_INIT_LISTS; i++) {  
        kmem_list3_init(&initkmem_list3);  
        /* 全局變數cache_cache指向的slab cache包含所有struct kmem_cache對象,不包含cache_cache本身
        。這裡初始化所有內存節點的struct kmem_cache的slab三鏈為空。*/  
        if (i < MAX_NUMNODES)  
            cache_cache.nodelists = NULL;  
    }  
    /* 設置struct kmem_cache的slab三鏈指向initkmem_list3中的一組slab三鏈,
    CACHE_CACHE為cache在內核cache鏈表中的索引,
    struct kmem_cache對應的cache是內核中創建的第一個cache
    ,故CACHE_CACHE為0 */  
    set_up_list3s(&cache_cache, CACHE_CACHE);  
  
    /*
     * Fragmentation resistance on low memory - only use bigger
     * page orders on machines with more than 32MB of memory.
     */  
     /* 全局變數slab_break_gfp_order為每個slab最多佔用幾個頁面
     ,用來抑制碎片,比如大小為3360的對象
     ,如果其slab只佔一個頁面,碎片為736
     ,slab佔用兩個頁面,則碎片大小也翻倍
     。只有當對象很大
     ,以至於slab中連一個對象都放不下時
     ,才可以超過這個值
     。有兩個可能的取值
     :當可用內存大於32MB時
     ,BREAK_GFP_ORDER_HI為1
     ,即每個slab最多佔用2個頁面
     ,只有當對象大小大於8192時
     ,才可以突破slab_break_gfp_order的限制
     。小於等於32MB時BREAK_GFP_ORDER_LO為0。*/  
    if (totalram_pages > (32 << 20) >> PAGE_SHIFT)  
        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();  
  
    /* 1) create the cache_cache */  
    /* 第一步,創建struct kmem_cache所在的cache,由全局變數cache_cache指向
    ,這裡只是初始化數據結構
    ,並未真正創建這些對象,要待分配時才創建。*/  
    /* 全局變數cache_chain是內核slab cache鏈表的表頭 */  
    INIT_LIST_HEAD(&cache_chain);  
      
    /* 將cache_cache加入到slab cache鏈表 */  
    list_add(&cache_cache.next, &cache_chain);  
  
    /* 設置cache著色基本單位為cache line的大小:32位元組 */  
    cache_cache.colour_off = cache_line_size();  
    /*  初始化cache_cache的local cache,同樣這裡也不能使用kmalloc
    ,需要使用靜態分配的全局變數initarray_cache */  
    cache_cache.array = &initarray_cache.cache;  
    /* 初始化slab鏈表 ,用全局變數*/  
    cache_cache.nodelists = &initkmem_list3;  
  
    /*
     * struct kmem_cache size depends on nr_node_ids, which
     * can be less than MAX_NUMNODES.
     */  
     /* buffer_size保存slab中對象的大小,這裡是計算struct kmem_cache的大小
     , nodelists是最後一個成員
     ,nr_node_ids保存內存節點個數,UMA為1
     ,所以nodelists偏移加上1個struct kmem_list3 的大小即為struct kmem_cache的大小 */  
    cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +  
                 nr_node_ids * sizeof(struct kmem_list3 *);  
#if DEBUG   
    cache_cache.obj_size = cache_cache.buffer_size;  
#endif   
    /* 將對象大小與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);  
  
    for (order = 0; order < MAX_ORDER; order++) {  
        /* 計算cache_cache中的對象數目 */  
        cache_estimate(order, cache_cache.buffer_size,  
            cache_line_size(), 0, &left_over, &cache_cache.num);  
        /* num不為0意味著創建struct kmem_cache對象成功,退出 */  
        if (cache_cache.num)  
            break;  
    }  
    BUG_ON(!cache_cache.num);  
     /* gfporder表示本slab包含2^gfporder個頁面 */  
    cache_cache.gfporder = order;  
      /* 著色區的大小,以colour_off為單位 */  
    cache_cache.colour = left_over / cache_cache.colour_off;  
    /* slab管理對象的大小 */  
    cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +  
                      sizeof(struct slab), cache_line_size());  
  
    /* 2+3) create the kmalloc caches */  
    /* 第二步,創建kmalloc所用的general cache
    ,kmalloc所用的對象按大小分級
    ,malloc_sizes保存大小,cache_names保存cache名 */  
    sizes = malloc_sizes;  
    names = cache_names;  
  
    /*
     * 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.cs_cachep = kmem_cache_create(names.name,  
                    sizes.cs_size,  
                    ARCH_KMALLOC_MINALIGN,  
                    ARCH_KMALLOC_FLAGS|SLAB_PANIC,  
                    NULL);  
    /* 如果struct kmem_list3和struct arraycache_init對應的kmalloc size索引不同
    ,即大小屬於不同的級別
    ,則創建struct kmem_list3所用的cache,否則共用一個cache */  
    if (INDEX_AC != INDEX_L3) {  
        sizes.cs_cachep =  
            kmem_cache_create(names.name,  
                sizes.cs_size,  
                ARCH_KMALLOC_MINALIGN,  
                ARCH_KMALLOC_FLAGS|SLAB_PANIC,  
                NULL);  
    }  
    /* 創建完上述兩個general cache后,slab early init階段結束,在此之前
    ,不允許創建外置式slab */  
    slab_early_init = 0;  
  
    /* 循環創建kmalloc各級別的general cache */  
    while (sizes->cs_size != 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.
         */  
         /* 某級別的kmalloc cache還未創建,創建之,struct kmem_list3和
         struct arraycache_init對應的cache已經創建過了 */  
        if (!sizes->cs_cachep) {  
            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.cs_cachep的local cache指向initarray_generic.cache
    ,參見setup_cpu_cache函數。這裡替換它們。*/  
    {  
        struct array_cache *ptr;  
        /* 申請cache_cache所用local cache的空間 */  
        ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);  
  
        BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);  
        /* 複製原cache_cache的local cache,即initarray_cache,到新的位置 */  
        memcpy(ptr, cpu_cache_get(&cache_cache),  
               sizeof(struct arraycache_init));  
        /*
         * Do not assume that spinlocks can be initialized via memcpy:
         */  
        spin_lock_init(&ptr->lock);  
        /* cache_cache的local cache指向新的位置 */  
        cache_cache.array = ptr;  
        /* 申請malloc_sizes.cs_cachep所用local cache的空間 */  
        ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);  
  
        BUG_ON(cpu_cache_get(malloc_sizes.cs_cachep)  
               != &initarray_generic.cache);  
        /* 複製原local cache到新分配的位置,注意此時local cache的大小是固定的 */  
        memcpy(ptr, cpu_cache_get(malloc_sizes.cs_cachep),  
               sizeof(struct arraycache_init));  
        /*
         * Do not assume that spinlocks can be initialized via memcpy:
         */  
        spin_lock_init(&ptr->lock);  
  
        malloc_sizes.cs_cachep->array =  
            ptr;  
    }  
    /* 5) Replace the bootstrap kmem_list3's */  
    /* 第五步,與第四步類似,用kmalloc的空間替換靜態分配的slab三鏈 */  
    {  
        int nid;  
      /* UMA只有一個節點 */  
        for_each_online_node(nid) {  
            /* 複製struct kmem_cache的slab三鏈 */  
            init_list(&cache_cache, &initkmem_list3, nid);  
            /* 複製struct arraycache_init的slab三鏈 */  
            init_list(malloc_sizes.cs_cachep,  
                  &initkmem_list3, nid);  
            /* 複製struct kmem_list3的slab三鏈 */  
            if (INDEX_AC != INDEX_L3) {  
                init_list(malloc_sizes.cs_cachep,  
                      &initkmem_list3, nid);  
            }  
        }  
    }  
    /* 更新slab系統初始化進度 */  
    g_cpucache_up = EARLY;  
}  
/*
* Initialisation.  Called after the page allocator have been initialised and
* before smp_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;
        /*初始化所有node的所有slab中的三個鏈表*/
        for (i = 0; i < NUM_INIT_LISTS; i++) {
                kmem_list3_init(&initkmem_list3);
                /* 全局變數cache_cache指向的slab cache包含所有struct kmem_cache對象,不包含cache_cache本身
                。這裡初始化所有內存節點的struct kmem_cache的slab三鏈為空。*/
                if (i < MAX_NUMNODES)
                        cache_cache.nodelists = NULL;
        }
        /* 設置struct kmem_cache的slab三鏈指向initkmem_list3中的一組slab三鏈,
        CACHE_CACHE為cache在內核cache鏈表中的索引,
        struct kmem_cache對應的cache是內核中創建的第一個cache
        ,故CACHE_CACHE為0 */
        set_up_list3s(&cache_cache, CACHE_CACHE);

        /*
         * Fragmentation resistance on low memory - only use bigger
         * page orders on machines with more than 32MB of memory.
         */
         /* 全局變數slab_break_gfp_order為每個slab最多佔用幾個頁面
         ,用來抑制碎片,比如大小為3360的對象
         ,如果其slab只佔一個頁面,碎片為736
         ,slab佔用兩個頁面,則碎片大小也翻倍
         。只有當對象很大
         ,以至於slab中連一個對象都放不下時
         ,才可以超過這個值
         。有兩個可能的取值
         :當可用內存大於32MB時
         ,BREAK_GFP_ORDER_HI為1
         ,即每個slab最多佔用2個頁面
         ,只有當對象大小大於8192時
         ,才可以突破slab_break_gfp_order的限制
         。小於等於32MB時BREAK_GFP_ORDER_LO為0。*/
        if (totalram_pages > (32 << 20) >> PAGE_SHIFT)
                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();

        /* 1) create the cache_cache */
        /* 第一步,創建struct kmem_cache所在的cache,由全局變數cache_cache指向
        ,這裡只是初始化數據結構
        ,並未真正創建這些對象,要待分配時才創建。*/
        /* 全局變數cache_chain是內核slab cache鏈表的表頭 */
        INIT_LIST_HEAD(&cache_chain);
       
        /* 將cache_cache加入到slab cache鏈表 */
        list_add(&cache_cache.next, &cache_chain);

        /* 設置cache著色基本單位為cache line的大小:32位元組 */
        cache_cache.colour_off = cache_line_size();
        /*  初始化cache_cache的local cache,同樣這裡也不能使用kmalloc
        ,需要使用靜態分配的全局變數initarray_cache */
        cache_cache.array = &initarray_cache.cache;
        /* 初始化slab鏈表 ,用全局變數*/
        cache_cache.nodelists = &initkmem_list3;

        /*
         * struct kmem_cache size depends on nr_node_ids, which
         * can be less than MAX_NUMNODES.
         */
         /* buffer_size保存slab中對象的大小,這裡是計算struct kmem_cache的大小
         , nodelists是最後一個成員
         ,nr_node_ids保存內存節點個數,UMA為1
         ,所以nodelists偏移加上1個struct kmem_list3 的大小即為struct kmem_cache的大小 */
        cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
                                 nr_node_ids * sizeof(struct kmem_list3 *);
#if DEBUG
        cache_cache.obj_size = cache_cache.buffer_size;
#endif
        /* 將對象大小與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);

        for (order = 0; order < MAX_ORDER; order++) {
                /* 計算cache_cache中的對象數目 */
                cache_estimate(order, cache_cache.buffer_size,
                        cache_line_size(), 0, &left_over, &cache_cache.num);
                /* num不為0意味著創建struct kmem_cache對象成功,退出 */
                if (cache_cache.num)
                        break;
        }
        BUG_ON(!cache_cache.num);
         /* gfporder表示本slab包含2^gfporder個頁面 */
        cache_cache.gfporder = order;
          /* 著色區的大小,以colour_off為單位 */
        cache_cache.colour = left_over / cache_cache.colour_off;
        /* slab管理對象的大小 */
        cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
                                      sizeof(struct slab), cache_line_size());

        /* 2+3) create the kmalloc caches */
        /* 第二步,創建kmalloc所用的general cache
        ,kmalloc所用的對象按大小分級
        ,malloc_sizes保存大小,cache_names保存cache名 */
        sizes = malloc_sizes;
        names = cache_names;

        /*
         * 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.cs_cachep = kmem_cache_create(names.name,
                                        sizes.cs_size,
                                        ARCH_KMALLOC_MINALIGN,
                                        ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                                        NULL);
        /* 如果struct kmem_list3和struct arraycache_init對應的kmalloc size索引不同
        ,即大小屬於不同的級別
        ,則創建struct kmem_list3所用的cache,否則共用一個cache */
        if (INDEX_AC != INDEX_L3) {
                sizes.cs_cachep =
                        kmem_cache_create(names.name,
                                sizes.cs_size,
                                ARCH_KMALLOC_MINALIGN,
                                ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                                NULL);
        }
        /* 創建完上述兩個general cache后,slab early init階段結束,在此之前
        ,不允許創建外置式slab */
        slab_early_init = 0;

        /* 循環創建kmalloc各級別的general cache */
        while (sizes->cs_size != 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.
                 */
                 /* 某級別的kmalloc cache還未創建,創建之,struct kmem_list3和
                 struct arraycache_init對應的cache已經創建過了 */
                if (!sizes->cs_cachep) {
                        sizes->cs_cachep = kmem_cache_create(names->name,
                                        sizes->cs_size,
                                        ARCH_KMALLOC_MINALIGN,
                                        ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                                        NULL);
                }
《解決方案》

謝謝分享




[火星人 via ] Linux內存管理之slab機制(初始化)已經有334次圍觀

http://www.coctec.com/docs/service/show-post-1161.html