Linux內存管理之slab機制(釋放對象)

火星人 @ 2014-03-03 , reply:0


Linux內存管理之slab機制(釋放對象)


Linux內存管理之slab機制(釋放對象)

Linux內核中將對象釋放到slab中上層所用函數為kfree()或kmem_cache_free()。兩個函數都會調用__cache_free()函數。
代碼執行流程:

1,當本地CPU cache中空閑對象數小於規定上限時,只需將對象放入本地CPU cache中;

2,當local cache中對象過多(大於等於規定上限),需要釋放一批對象到slab三鏈中。由函數cache_flusharray()實現。

1)如果三鏈中存在共享本地cache,那麼首先選擇釋放到共享本地cache中,能釋放多少是多少;

2)如果沒有shared local cache,釋放對象到slab三鏈中,實現函數為free_block()。對於free_block()函數,當三鏈中的空閑對象數過多時,銷毀此cache。不然,添加此slab到空閑鏈表。因為在分配的時候我們看到將slab結構從cache鏈表中脫離了,在這裡,根據page描述符的lru找到slab並將它添加到三鏈的空閑鏈表中。

主實現



view plaincopy to clipboard01./*
02. * Release an obj back to its cache. If the obj has a constructed state, it must
03. * be in this state _before_ it is released.  Called with disabled ints.
04. */  
05.static inline void __cache_free(struct kmem_cache *cachep, void *objp)  
06.{  
07.    /* 獲得本CPU的local cache */  
08.    struct array_cache *ac = cpu_cache_get(cachep);  
09.  
10.    check_irq_off();  
11.    kmemleak_free_recursive(objp, cachep->flags);  
12.    objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));  
13.  
14.    kmemcheck_slab_free(cachep, objp, obj_size(cachep));  
15.  
16.    /*
17.     * Skip calling cache_free_alien() when the platform is not numa.
18.     * This will avoid cache misses that happen while accessing slabp (which
19.     * is per page memory  reference) to get nodeid. Instead use a global
20.     * variable to skip the call, which is mostly likely to be present in
21.     * the cache.
22.     *//* NUMA相關 */  
23.    if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))  
24.        return;  
25.  
26.    if (likely(ac->avail < ac->limit)) {  
27.        /* local cache中的空閑對象數小於上限時
28.        ,只需將對象釋放回entry數組中 */  
29.        STATS_INC_FREEHIT(cachep);  
30.        ac->entry = objp;  
31.        return;  
32.    } else {  
33.        /* 大於等於上限時, */  
34.        STATS_INC_FREEMISS(cachep);  
35.        /* local cache中對象過多,需要釋放一批對象到slab三鏈中。*/  
36.        cache_flusharray(cachep, ac);  
37.        ac->entry = objp;  
38.    }  
39.}  釋放對象到三鏈中



view plaincopy to clipboard01./*local cache中對象過多,需要釋放一批對象到slab三鏈中。*/  
02.static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)  
03.{  
04.    int batchcount;  
05.    struct kmem_list3 *l3;  
06.    int node = numa_node_id();  
07.    /* 每次釋放多少個對象 */  
08.    batchcount = ac->batchcount;  
09.#if DEBUG  
10.    BUG_ON(!batchcount || batchcount > ac->avail);  
11.#endif  
12.    check_irq_off();  
13.    /* 獲得此cache的slab三鏈 */  
14.    l3 = cachep->nodelists;  
15.    spin_lock(&l3->list_lock);  
16.    if (l3->shared) {  
17.        /* 如果存在shared local cache,將對象釋放到其中 */  
18.        struct array_cache *shared_array = l3->shared;  
19.        /* 計算shared local cache中還有多少空位 */  
20.        int max = shared_array->limit - shared_array->avail;  
21.        if (max) {  
22.            /* 空位數小於要釋放的對象數時,釋放數等於空位數 */  
23.            if (batchcount > max)  
24.                batchcount = max;  
25.            /* 釋放local cache前面的幾個對象到shared local cache中
26.            ,前面的是最早不用的 */  
27.            memcpy(&(shared_array->entry),  
28.                   ac->entry, sizeof(void *) * batchcount);  
29.            /* 增加shared local cache可用對象數 */  
30.            shared_array->avail += batchcount;  
31.            goto free_done;  
32.        }  
33.    }  
34.    /* 無shared local cache,釋放對象到slab三鏈中 */  
35.    free_block(cachep, ac->entry, batchcount, node);  
36.free_done:  
37.#if STATS  
38.    {  
39.        int i = 0;  
40.        struct list_head *p;  
41.  
42.        p = l3->slabs_free.next;  
43.        while (p != &(l3->slabs_free)) {  
44.            struct slab *slabp;  
45.  
46.            slabp = list_entry(p, struct slab, list);  
47.            BUG_ON(slabp->inuse);  
48.  
49.            i++;  
50.            p = p->next;  
51.        }  
52.        STATS_SET_FREEABLE(cachep, i);  
53.    }  
54.#endif  
55.    spin_unlock(&l3->list_lock);  
56.    /* 減少local cache可用對象數*/  
57.    ac->avail -= batchcount;  
58.    /* local cache前面有batchcount個空位,將後面的對象依次前移batchcount位 */  
59.    memmove(ac->entry, &(ac->entry), sizeof(void *)*ac->avail);  
60.}  無shared local cache,釋放對象到slab三鏈中



view plaincopy to clipboard01./*
02. * Caller needs to acquire correct kmem_list's list_lock
03. */  
04. /*釋放一定數目的對象*/  
05.static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,  
06.               int node)  
07.{  
08.    int i;  
09.    struct kmem_list3 *l3;  
10.     /* 逐一釋放對象到slab三鏈中 */  
11.    for (i = 0; i < nr_objects; i++) {  
12.        void *objp = objpp;  
13.        struct slab *slabp;  
14.        /* 通過虛擬地址得到page,再通過page得到slab */  
15.        slabp = virt_to_slab(objp);  
16.        /* 獲得slab三鏈 */  
17.        l3 = cachep->nodelists;  
18.        /* 先將對象所在的slab從鏈表中摘除 */  
19.        list_del(&slabp->list);  
20.        check_spinlock_acquired_node(cachep, node);  
21.        check_slabp(cachep, slabp);  
22.        /* 將對象釋放到其slab中 */  
23.        slab_put_obj(cachep, slabp, objp, node);  
24.        STATS_DEC_ACTIVE(cachep);  
25.        /* 空閑對象數加一 */  
26.        l3->free_objects++;  
27.        check_slabp(cachep, slabp);  
28.  
29.        /* fixup slab chains */  
30.        if (slabp->inuse == 0) {  
31.             /* 如果slab中均為空閑對象 */  
32.            if (l3->free_objects > l3->free_limit) {  
33.                /* 如果slab三鏈中空閑對象數超過上限
34.                ,直接回收整個slab到內存
35.                ,空閑對象數減去每個slab中對象數 */  
36.                l3->free_objects -= cachep->num;  
37.                /* No need to drop any previously held
38.                 * lock here, even if we have a off-slab slab
39.                 * descriptor it is guaranteed to come from
40.                 * a different cache, refer to comments before
41.                 * alloc_slabmgmt.
42.                 *//* 銷毀struct slab對象 */  
43.                slab_destroy(cachep, slabp);  
44.            } else {  
45.                /* 將此slab添加到空slab鏈表中 */  
46.                list_add(&slabp->list, &l3->slabs_free);  
47.            }  
48.        } else {  
49.            /* Unconditionally move a slab to the end of the
50.             * partial list on free - maximum time for the
51.             * other objects to be freed, too.
52.             *//*將此slab添加到部分滿slab鏈表中*/  
53.            list_add_tail(&slabp->list, &l3->slabs_partial);  
54.        }  
55.    }  
56.}  將對象釋放到其slab中



view plaincopy to clipboard01.static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,  
02.                void *objp, int nodeid)  
03.{   /* 獲得對象在kmem_bufctl_t數組中的索引 */  
04.    unsigned int objnr = obj_to_index(cachep, slabp, objp);  
05.  
06.#if DEBUG  
07.    /* Verify that the slab belongs to the intended node */  
08.    WARN_ON(slabp->nodeid != nodeid);  
09.  
10.    if (slab_bufctl(slabp) + 1 <= SLAB_LIMIT + 1) {  
11.        printk(KERN_ERR "slab: double free detected in cache "  
12.                "'%s', objp %p\n", cachep->name, objp);  
13.        BUG();  
14.    }  
15.#endif  
16.    /*這兩步相當於靜態鏈表的插入操作*/  
17.    /* 指向slab中原來的第一個空閑對象 */  
18.    slab_bufctl(slabp) = slabp->free;  
19.    /* 釋放的對象作為第一個空閑對象 */  
20.    slabp->free = objnr;  
21.    /* 已分配對象數減一 */  
22.    slabp->inuse--;  
23.}  輔助函數



view plaincopy to clipboard01./* 通過虛擬地址得到page,再通過page得到slab */  
02.static inline struct slab *virt_to_slab(const void *obj)  
03.{  
04.    struct page *page = virt_to_head_page(obj);  
05.    return page_get_slab(page);  
06.}  view plaincopy to clipboard01.static inline struct slab *page_get_slab(struct page *page)  
02.{  
03.    BUG_ON(!PageSlab(page));  
04.    return (struct slab *)page->lru.prev;  
05.}  
可見,用page->lru.prev得到slab,和創建slab時相呼應。
《解決方案》

謝謝分享




[火星人 via ] Linux內存管理之slab機制(釋放對象)已經有170次圍觀

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