歡迎您光臨本站 註冊首頁

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

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

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


第二階段代碼分析Start_kernel()->kmem_cache_init_late()

view plaincopy to clipboardprint?/*Slab系統初始化分兩個部分,先初始化一些基本的,待系統初始化工作進行的差不多時,再配置一些特殊功能。*/  
void __init kmem_cache_init_late(void)  
{  
    struct kmem_cache *cachep;  
    /* 初始化階段local cache的大小是固定的,要根據對象大小重新計算 */  
    /* 6) resize the head arrays to their final sizes */  
    mutex_lock(&cache_chain_mutex);  
    list_for_each_entry(cachep, &cache_chain, next)  
        if (enable_cpucache(cachep, GFP_NOWAIT))  
            BUG();  
    mutex_unlock(&cache_chain_mutex);  
  
    /* Done! */  
    /* 大功告成,general cache終於全部建立起來了 */  
    g_cpucache_up = FULL;  
  
    /* Annotate slab for lockdep -- annotate the malloc caches */  
    init_lock_keys();  
  
    /*
     * Register a cpu startup notifier callback that initializes
     * cpu_cache_get for all new cpus
     */  
     /* 註冊cpu up回調函數,cpu up時配置local cache */  
    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系統初始化分兩個部分,先初始化一些基本的,待系統初始化工作進行的差不多時,再配置一些特殊功能。*/
void __init kmem_cache_init_late(void)
{
        struct kmem_cache *cachep;
        /* 初始化階段local cache的大小是固定的,要根據對象大小重新計算 */
        /* 6) resize the head arrays to their final sizes */
        mutex_lock(&cache_chain_mutex);
        list_for_each_entry(cachep, &cache_chain, next)
                if (enable_cpucache(cachep, GFP_NOWAIT))
                        BUG();
        mutex_unlock(&cache_chain_mutex);

        /* Done! */
        /* 大功告成,general cache終於全部建立起來了 */
        g_cpucache_up = FULL;

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

        /*
         * Register a cpu startup notifier callback that initializes
         * cpu_cache_get for all new cpus
         */
         /* 註冊cpu up回調函數,cpu up時配置local cache */
        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.
         */
}view plaincopy to clipboardprint?/* Called with cache_chain_mutex held always */  
/*local cache 初始化*/  
static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)  
{  
    int err;  
    int limit, shared;  
  
    /*
     * The head array serves three purposes:
     * - create a LIFO ordering, i.e. return objects that are cache-warm
     * - reduce the number of spinlock operations.
     * - reduce the number of linked list operations on the slab and
     *   bufctl chains: array operations are cheaper.
     * The numbers are guessed, we should auto-tune as described by
     * Bonwick.
     */ /* 根據對象大小計算local cache中對象數目上限 */  
    if (cachep->buffer_size > 131072)  
        limit = 1;  
    else if (cachep->buffer_size > PAGE_SIZE)  
        limit = 8;  
    else if (cachep->buffer_size > 1024)  
        limit = 24;  
    else if (cachep->buffer_size > 256)  
        limit = 54;  
    else  
        limit = 120;  
  
    /*
     * CPU bound tasks (e.g. network routing) can exhibit cpu bound
     * allocation behaviour: Most allocs on one cpu, most free operations
     * on another cpu. For these cases, an efficient object passing between
     * cpus is necessary. This is provided by a shared array. The array
     * replaces Bonwick's magazine layer.
     * On uniprocessor, it's functionally equivalent (but less efficient)
     * to a larger limit. Thus disabled by default.
     */  
    shared = 0;  
    /* 多核系統,設置shared local cache中對象數目 */  
    if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)  
        shared = 8;  
  
#if DEBUG   
    /*
     * With debugging enabled, large batchcount lead to excessively long
     * periods with disabled local interrupts. Limit the batchcount
     */  
    if (limit > 32)  
        limit = 32;  
#endif   
    /* 配置local cache */  
    err = do_tune_cpucache(cachep, limit, (limit + 1) / 2, shared, gfp);  
    if (err)  
        printk(KERN_ERR "enable_cpucache failed for %s, error %d.\n",  
               cachep->name, -err);  
    return err;  
}  
/* Called with cache_chain_mutex held always */
/*local cache 初始化*/
static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp)
{
        int err;
        int limit, shared;

        /*
         * The head array serves three purposes:
         * - create a LIFO ordering, i.e. return objects that are cache-warm
         * - reduce the number of spinlock operations.
         * - reduce the number of linked list operations on the slab and
         *   bufctl chains: array operations are cheaper.
         * The numbers are guessed, we should auto-tune as described by
         * Bonwick.
         */ /* 根據對象大小計算local cache中對象數目上限 */
        if (cachep->buffer_size > 131072)
                limit = 1;
        else if (cachep->buffer_size > PAGE_SIZE)
                limit = 8;
        else if (cachep->buffer_size > 1024)
                limit = 24;
        else if (cachep->buffer_size > 256)
                limit = 54;
        else
                limit = 120;

        /*
         * CPU bound tasks (e.g. network routing) can exhibit cpu bound
         * allocation behaviour: Most allocs on one cpu, most free operations
         * on another cpu. For these cases, an efficient object passing between
         * cpus is necessary. This is provided by a shared array. The array
         * replaces Bonwick's magazine layer.
         * On uniprocessor, it's functionally equivalent (but less efficient)
         * to a larger limit. Thus disabled by default.
         */
        shared = 0;
        /* 多核系統,設置shared local cache中對象數目 */
        if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)
                shared = 8;

#if DEBUG
        /*
         * With debugging enabled, large batchcount lead to excessively long
         * periods with disabled local interrupts. Limit the batchcount
         */
        if (limit > 32)
                limit = 32;
#endif
        /* 配置local cache */
        err = do_tune_cpucache(cachep, limit, (limit + 1) / 2, shared, gfp);
        if (err)
                printk(KERN_ERR "enable_cpucache failed for %s, error %d.\n",
                       cachep->name, -err);
        return err;
}view plaincopy to clipboardprint?/* Always called with the cache_chain_mutex held */  
/*配置local cache、shared local cache和slab三鏈*/  
static int do_tune_cpucache(struct kmem_cache *cachep, int limit,  
                int batchcount, int shared, gfp_t gfp)  
{  
    struct ccupdate_struct *new;  
    int i;  
  
    new = kzalloc(sizeof(*new), gfp);  
    if (!new)  
        return -ENOMEM;  
    /* 為每個cpu分配新的struct array_cache對象 */  
    for_each_online_cpu(i) {  
        new->new = alloc_arraycache(cpu_to_node(i), limit,  
                        batchcount, gfp);  
        if (!new->new) {  
            for (i--; i >= 0; i--)  
                kfree(new->new);  
            kfree(new);  
            return -ENOMEM;  
        }  
    }  
    new->cachep = cachep;  
    /* 用新的struct array_cache對象替換舊的struct array_cache對象
    ,在支持cpu熱插拔的系統上,離線cpu可能沒有釋放local cache
    ,使用的仍是舊local cache,參見__kmem_cache_destroy函數
    。雖然cpu up時要重新配置local cache,也無濟於事。考慮下面的情景
    :共有Cpu A和Cpu B,Cpu B down后,destroy Cache X,由於此時Cpu B是down狀態
    ,所以Cache X中Cpu B的local cache未釋放,過一段時間Cpu B又up了
    ,更新cache_chain 鏈中所有cache的local cache,但此時Cache X對象已經釋放回
    cache_cache中了,其Cpu B local cache並未被更新。又過了一段時間
    ,系統需要創建新的cache,將Cache X對象分配出去,其Cpu B仍然是舊的
    local cache,需要進行更新。
    */  
    on_each_cpu(do_ccupdate_local, (void *)new, 1);  
  
    check_irq_on();  
    cachep->batchcount = batchcount;  
    cachep->limit = limit;  
    cachep->shared = shared;  
    /* 釋放舊的local cache */  
    for_each_online_cpu(i) {  
        struct array_cache *ccold = new->new;  
        if (!ccold)  
            continue;  
        spin_lock_irq(&cachep->nodelists->list_lock);  
        /* 釋放舊local cache中的對象 */  
        free_block(cachep, ccold->entry, ccold->avail, cpu_to_node(i));  
        spin_unlock_irq(&cachep->nodelists->list_lock);  
        /* 釋放舊的struct array_cache對象 */  
        kfree(ccold);  
    }  
    kfree(new);  
    /* 初始化shared local cache 和slab三鏈 */  
    return alloc_kmemlist(cachep, gfp);  
}  
/* Always called with the cache_chain_mutex held */
/*配置local cache、shared local cache和slab三鏈*/
static int do_tune_cpucache(struct kmem_cache *cachep, int limit,
                                int batchcount, int shared, gfp_t gfp)
{
        struct ccupdate_struct *new;
        int i;

        new = kzalloc(sizeof(*new), gfp);
        if (!new)
                return -ENOMEM;
        /* 為每個cpu分配新的struct array_cache對象 */
        for_each_online_cpu(i) {
                new->new = alloc_arraycache(cpu_to_node(i), limit,
                                                batchcount, gfp);
                if (!new->new) {
                        for (i--; i >= 0; i--)
                                kfree(new->new);
                        kfree(new);
                        return -ENOMEM;
                }
        }
        new->cachep = cachep;
        /* 用新的struct array_cache對象替換舊的struct array_cache對象
        ,在支持cpu熱插拔的系統上,離線cpu可能沒有釋放local cache
        ,使用的仍是舊local cache,參見__kmem_cache_destroy函數
        。雖然cpu up時要重新配置local cache,也無濟於事。考慮下面的情景
        :共有Cpu A和Cpu B,Cpu B down后,destroy Cache X,由於此時Cpu B是down狀態
        ,所以Cache X中Cpu B的local cache未釋放,過一段時間Cpu B又up了
        ,更新cache_chain 鏈中所有cache的local cache,但此時Cache X對象已經釋放回
        cache_cache中了,其Cpu B local cache並未被更新。又過了一段時間
        ,系統需要創建新的cache,將Cache X對象分配出去,其Cpu B仍然是舊的
        local cache,需要進行更新。
        */
        on_each_cpu(do_ccupdate_local, (void *)new, 1);

        check_irq_on();
        cachep->batchcount = batchcount;
        cachep->limit = limit;
        cachep->shared = shared;
        /* 釋放舊的local cache */
        for_each_online_cpu(i) {
                struct array_cache *ccold = new->new;
                if (!ccold)
                        continue;
                spin_lock_irq(&cachep->nodelists->list_lock);
                /* 釋放舊local cache中的對象 */
                free_block(cachep, ccold->entry, ccold->avail, cpu_to_node(i));
                spin_unlock_irq(&cachep->nodelists->list_lock);
                /* 釋放舊的struct array_cache對象 */
                kfree(ccold);
        }
        kfree(new);
        /* 初始化shared local cache 和slab三鏈 */
        return alloc_kmemlist(cachep, gfp);
} 更新本地cacheview plaincopy to clipboardprint?/*更新每個cpu的struct array_cache對象*/  
static void do_ccupdate_local(void *info)  
{  
    struct ccupdate_struct *new = info;  
    struct array_cache *old;  
  
    check_irq_off();  
    old = cpu_cache_get(new->cachep);  
     /* 指向新的struct array_cache對象 */  
    new->cachep->array = new->new;  
        /* 保存舊的struct array_cache對象 */  
    new->new = old;  
}  
/*更新每個cpu的struct array_cache對象*/
static void do_ccupdate_local(void *info)
{
        struct ccupdate_struct *new = info;
        struct array_cache *old;

        check_irq_off();
        old = cpu_cache_get(new->cachep);
         /* 指向新的struct array_cache對象 */
        new->cachep->array = new->new;
            /* 保存舊的struct array_cache對象 */
        new->new = old;
}view plaincopy to clipboardprint?/*初始化shared local cache和slab三鏈,初始化完成後,slab三鏈中沒有任何slab*/  
static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)  
{  
    int node;  
    struct kmem_list3 *l3;  
    struct array_cache *new_shared;  
    struct array_cache **new_alien = NULL;  
  
    for_each_online_node(node) {  
         /* NUMA相關 */  
                if (use_alien_caches) {  
                        new_alien = alloc_alien_cache(node, cachep->limit, gfp);  
                        if (!new_alien)  
                                goto fail;  
                }  
  
        new_shared = NULL;  
        if (cachep->shared) {  
            /* 分配shared local cache */  
            new_shared = alloc_arraycache(node,  
                cachep->shared*cachep->batchcount,  
                    0xbaadf00d, gfp);  
            if (!new_shared) {  
                free_alien_cache(new_alien);  
                goto fail;  
            }  
        }  
        /* 獲得舊的slab三鏈 */  
        l3 = cachep->nodelists;  
        if (l3) {  
            /* 就slab三鏈指針不為空,需要先釋放舊的資源 */  
            struct array_cache *shared = l3->shared;  
  
            spin_lock_irq(&l3->list_lock);  
            /* 釋放舊的shared local cache中的對象 */  
            if (shared)  
                free_block(cachep, shared->entry,  
                        shared->avail, node);  
            /* 指向新的shared local cache */  
            l3->shared = new_shared;  
            if (!l3->alien) {  
                l3->alien = new_alien;  
                new_alien = NULL;  
            }/* 計算cache中空閑對象的上限 */  
            l3->free_limit = (1 + nr_cpus_node(node)) *  
                    cachep->batchcount + cachep->num;  
            spin_unlock_irq(&l3->list_lock);  
            /* 釋放舊shared local cache的struct array_cache對象 */  
            kfree(shared);  
            free_alien_cache(new_alien);  
            continue;/*訪問下一個節點*/  
        }  
         /* 如果沒有舊的l3,分配新的slab三鏈 */  
        l3 = kmalloc_node(sizeof(struct kmem_list3), gfp, node);  
        if (!l3) {  
            free_alien_cache(new_alien);  
            kfree(new_shared);  
            goto fail;  
        }  
         /* 初始化slab三鏈 */  
        kmem_list3_init(l3);  
        l3->next_reap = jiffies + REAPTIMEOUT_LIST3 +  
                ((unsigned long)cachep) % REAPTIMEOUT_LIST3;  
        l3->shared = new_shared;  
        l3->alien = new_alien;  
        l3->free_limit = (1 + nr_cpus_node(node)) *  
                    cachep->batchcount + cachep->num;  
        cachep->nodelists = l3;  
    }  
    return 0;  
  
fail:  
    if (!cachep->next.next) {  
        /* Cache is not active yet. Roll back what we did */  
        node--;  
        while (node >= 0) {  
            if (cachep->nodelists) {  
                l3 = cachep->nodelists;  
  
                kfree(l3->shared);  
                free_alien_cache(l3->alien);  
                kfree(l3);  
                cachep->nodelists = NULL;  
            }  
            node--;  
        }  
    }  
    return -ENOMEM;  
}  
/*初始化shared local cache和slab三鏈,初始化完成後,slab三鏈中沒有任何slab*/
static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp)
{
        int node;
        struct kmem_list3 *l3;
        struct array_cache *new_shared;
        struct array_cache **new_alien = NULL;

        for_each_online_node(node) {
                 /* NUMA相關 */
                if (use_alien_caches) {
                        new_alien = alloc_alien_cache(node, cachep->limit, gfp);
                        if (!new_alien)
                                goto fail;
                }

                new_shared = NULL;
                if (cachep->shared) {
                        /* 分配shared local cache */
                        new_shared = alloc_arraycache(node,
                                cachep->shared*cachep->batchcount,
                                        0xbaadf00d, gfp);
                        if (!new_shared) {
                                free_alien_cache(new_alien);
                                goto fail;
                        }
                }
                /* 獲得舊的slab三鏈 */
                l3 = cachep->nodelists;
                if (l3) {
                        /* 就slab三鏈指針不為空,需要先釋放舊的資源 */
                        struct array_cache *shared = l3->shared;

                        spin_lock_irq(&l3->list_lock);
                        /* 釋放舊的shared local cache中的對象 */
                        if (shared)
                                free_block(cachep, shared->entry,
                                                shared->avail, node);
                        /* 指向新的shared local cache */
                        l3->shared = new_shared;
                        if (!l3->alien) {
                                l3->alien = new_alien;
                                new_alien = NULL;
                        }/* 計算cache中空閑對象的上限 */
                        l3->free_limit = (1 + nr_cpus_node(node)) *
                                        cachep->batchcount + cachep->num;
                        spin_unlock_irq(&l3->list_lock);
                        /* 釋放舊shared local cache的struct array_cache對象 */
                        kfree(shared);
                        free_alien_cache(new_alien);
                        continue;/*訪問下一個節點*/
                }
                 /* 如果沒有舊的l3,分配新的slab三鏈 */
                l3 = kmalloc_node(sizeof(struct kmem_list3), gfp, node);
                if (!l3) {
                        free_alien_cache(new_alien);
                        kfree(new_shared);
                        goto fail;
                }
                 /* 初始化slab三鏈 */
                kmem_list3_init(l3);
                l3->next_reap = jiffies + REAPTIMEOUT_LIST3 +
                                ((unsigned long)cachep) % REAPTIMEOUT_LIST3;
                l3->shared = new_shared;
                l3->alien = new_alien;
                l3->free_limit = (1 + nr_cpus_node(node)) *
                                        cachep->batchcount + cachep->num;
                cachep->nodelists = l3;
        }
        return 0;

fail:
        if (!cachep->next.next) {
                /* Cache is not active yet. Roll back what we did */
                node--;
                while (node >= 0) {
                        if (cachep->nodelists) {
                                l3 = cachep->nodelists;

                                kfree(l3->shared);
                                free_alien_cache(l3->alien);
                                kfree(l3);
                                cachep->nodelists = NULL;
                        }
                        node--;
                }
        }
        return -ENOMEM;
} 看一個輔助函數view plaincopy to clipboardprint?/*分配struct array_cache對象。*/  
static struct array_cache *alloc_arraycache(int node, int entries,  
                        int batchcount, gfp_t gfp)  
{  
    /* struct array_cache後面緊接著的是entry數組,合在一起申請內存 */  
    int memsize = sizeof(void *) * entries + sizeof(struct array_cache);  
    struct array_cache *nc = NULL;  
    /* 分配一個local cache對象,kmalloc從general cache中分配 */  
    nc = kmalloc_node(memsize, gfp, node);  
    /*
     * The array_cache structures contain pointers to free object.
     * However, when such objects are allocated or transfered to another
     * cache the pointers are not cleared and they could be counted as
     * valid references during a kmemleak scan. Therefore, kmemleak must
     * not scan such objects.
     */  
    kmemleak_no_scan(nc);  
     /* 初始化local cache */  
    if (nc) {  
        nc->avail = 0;  
        nc->limit = entries;  
        nc->batchcount = batchcount;  
        nc->touched = 0;  
        spin_lock_init(&nc->lock);  
    }  
    return nc;  
}  
/*分配struct array_cache對象。*/
static struct array_cache *alloc_arraycache(int node, int entries,
                                            int batchcount, gfp_t gfp)
{
        /* struct array_cache後面緊接著的是entry數組,合在一起申請內存 */
        int memsize = sizeof(void *) * entries + sizeof(struct array_cache);
        struct array_cache *nc = NULL;
        /* 分配一個local cache對象,kmalloc從general cache中分配 */
        nc = kmalloc_node(memsize, gfp, node);
        /*
         * The array_cache structures contain pointers to free object.
         * However, when such objects are allocated or transfered to another
         * cache the pointers are not cleared and they could be counted as
         * valid references during a kmemleak scan. Therefore, kmemleak must
         * not scan such objects.
         */
        kmemleak_no_scan(nc);
         /* 初始化local cache */
        if (nc) {
                nc->avail = 0;
                nc->limit = entries;
                nc->batchcount = batchcount;
                nc->touched = 0;
                spin_lock_init(&nc->lock);
        }
        return nc;
} 源代碼中涉及了slab的分配、釋放等操作在後面分析中陸續總結。slab相關數據結構、工作機制以及整體框架在分析完了slab的創建、釋放工作后再做總結,這樣可能會對slab機制有更好的了解。當然,從代碼中看運行機制會更有說服了,也是一種習慣。
《解決方案》

謝謝分享

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

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