Chinaunix

标题: Linux内存管理之slab机制(释放对象) [打印本页]

作者: feiyang10086    时间: 2012-01-10 20:01
标题: 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 clipboard
  1. 01./*
  2. 02. * Release an obj back to its cache. If the obj has a constructed state, it must
  3. 03. * be in this state _before_ it is released.  Called with disabled ints.
  4. 04. */  
  5. 05.static inline void __cache_free(struct kmem_cache *cachep, void *objp)  
  6. 06.{  
  7. 07.    /* 获得本CPU的local cache */  
  8. 08.    struct array_cache *ac = cpu_cache_get(cachep);  
  9. 09.  
  10. 10.    check_irq_off();  
  11. 11.    kmemleak_free_recursive(objp, cachep->flags);  
  12. 12.    objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));  
  13. 13.  
  14. 14.    kmemcheck_slab_free(cachep, objp, obj_size(cachep));  
  15. 15.  
  16. 16.    /*
  17. 17.     * Skip calling cache_free_alien() when the platform is not numa.
  18. 18.     * This will avoid cache misses that happen while accessing slabp (which
  19. 19.     * is per page memory  reference) to get nodeid. Instead use a global
  20. 20.     * variable to skip the call, which is mostly likely to be present in
  21. 21.     * the cache.
  22. 22.     *//* NUMA相关 */  
  23. 23.    if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))  
  24. 24.        return;  
  25. 25.  
  26. 26.    if (likely(ac->avail < ac->limit)) {  
  27. 27.        /* local cache中的空闲对象数小于上限时
  28. 28.        ,只需将对象释放回entry数组中 */  
  29. 29.        STATS_INC_FREEHIT(cachep);  
  30. 30.        ac->entry[ac->avail++] = objp;  
  31. 31.        return;  
  32. 32.    } else {  
  33. 33.        /* 大于等于上限时, */  
  34. 34.        STATS_INC_FREEMISS(cachep);  
  35. 35.        /* local cache中对象过多,需要释放一批对象到slab三链中。*/  
  36. 36.        cache_flusharray(cachep, ac);  
  37. 37.        ac->entry[ac->avail++] = objp;  
  38. 38.    }  
  39. 39.}  
复制代码
释放对象到三链中



view plaincopy to clipboard
  1. 01./*local cache中对象过多,需要释放一批对象到slab三链中。*/  
  2. 02.static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)  
  3. 03.{  
  4. 04.    int batchcount;  
  5. 05.    struct kmem_list3 *l3;  
  6. 06.    int node = numa_node_id();  
  7. 07.    /* 每次释放多少个对象 */  
  8. 08.    batchcount = ac->batchcount;  
  9. 09.#if DEBUG  
  10. 10.    BUG_ON(!batchcount || batchcount > ac->avail);  
  11. 11.#endif  
  12. 12.    check_irq_off();  
  13. 13.    /* 获得此cache的slab三链 */  
  14. 14.    l3 = cachep->nodelists[node];  
  15. 15.    spin_lock(&l3->list_lock);  
  16. 16.    if (l3->shared) {  
  17. 17.        /* 如果存在shared local cache,将对象释放到其中 */  
  18. 18.        struct array_cache *shared_array = l3->shared;  
  19. 19.        /* 计算shared local cache中还有多少空位 */  
  20. 20.        int max = shared_array->limit - shared_array->avail;  
  21. 21.        if (max) {  
  22. 22.            /* 空位数小于要释放的对象数时,释放数等于空位数 */  
  23. 23.            if (batchcount > max)  
  24. 24.                batchcount = max;  
  25. 25.            /* 释放local cache前面的几个对象到shared local cache中
  26. 26.            ,前面的是最早不用的 */  
  27. 27.            memcpy(&(shared_array->entry[shared_array->avail]),  
  28. 28.                   ac->entry, sizeof(void *) * batchcount);  
  29. 29.            /* 增加shared local cache可用对象数 */  
  30. 30.            shared_array->avail += batchcount;  
  31. 31.            goto free_done;  
  32. 32.        }  
  33. 33.    }  
  34. 34.    /* 无shared local cache,释放对象到slab三链中 */  
  35. 35.    free_block(cachep, ac->entry, batchcount, node);  
  36. 36.free_done:  
  37. 37.#if STATS  
  38. 38.    {  
  39. 39.        int i = 0;  
  40. 40.        struct list_head *p;  
  41. 41.  
  42. 42.        p = l3->slabs_free.next;  
  43. 43.        while (p != &(l3->slabs_free)) {  
  44. 44.            struct slab *slabp;  
  45. 45.  
  46. 46.            slabp = list_entry(p, struct slab, list);  
  47. 47.            BUG_ON(slabp->inuse);  
  48. 48.  
  49. 49.            i++;  
  50. 50.            p = p->next;  
  51. 51.        }  
  52. 52.        STATS_SET_FREEABLE(cachep, i);  
  53. 53.    }  
  54. 54.#endif  
  55. 55.    spin_unlock(&l3->list_lock);  
  56. 56.    /* 减少local cache可用对象数*/  
  57. 57.    ac->avail -= batchcount;  
  58. 58.    /* local cache前面有batchcount个空位,将后面的对象依次前移batchcount位 */  
  59. 59.    memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);  
  60. 60.}  
复制代码
无shared local cache,释放对象到slab三链中



view plaincopy to clipboard
  1. 01./*
  2. 02. * Caller needs to acquire correct kmem_list's list_lock
  3. 03. */  
  4. 04. /*释放一定数目的对象*/  
  5. 05.static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,  
  6. 06.               int node)  
  7. 07.{  
  8. 08.    int i;  
  9. 09.    struct kmem_list3 *l3;  
  10. 10.     /* 逐一释放对象到slab三链中 */  
  11. 11.    for (i = 0; i < nr_objects; i++) {  
  12. 12.        void *objp = objpp[i];  
  13. 13.        struct slab *slabp;  
  14. 14.        /* 通过虚拟地址得到page,再通过page得到slab */  
  15. 15.        slabp = virt_to_slab(objp);  
  16. 16.        /* 获得slab三链 */  
  17. 17.        l3 = cachep->nodelists[node];  
  18. 18.        /* 先将对象所在的slab从链表中摘除 */  
  19. 19.        list_del(&slabp->list);  
  20. 20.        check_spinlock_acquired_node(cachep, node);  
  21. 21.        check_slabp(cachep, slabp);  
  22. 22.        /* 将对象释放到其slab中 */  
  23. 23.        slab_put_obj(cachep, slabp, objp, node);  
  24. 24.        STATS_DEC_ACTIVE(cachep);  
  25. 25.        /* 空闲对象数加一 */  
  26. 26.        l3->free_objects++;  
  27. 27.        check_slabp(cachep, slabp);  
  28. 28.  
  29. 29.        /* fixup slab chains */  
  30. 30.        if (slabp->inuse == 0) {  
  31. 31.             /* 如果slab中均为空闲对象 */  
  32. 32.            if (l3->free_objects > l3->free_limit) {  
  33. 33.                /* 如果slab三链中空闲对象数超过上限
  34. 34.                ,直接回收整个slab到内存
  35. 35.                ,空闲对象数减去每个slab中对象数 */  
  36. 36.                l3->free_objects -= cachep->num;  
  37. 37.                /* No need to drop any previously held
  38. 38.                 * lock here, even if we have a off-slab slab
  39. 39.                 * descriptor it is guaranteed to come from
  40. 40.                 * a different cache, refer to comments before
  41. 41.                 * alloc_slabmgmt.
  42. 42.                 *//* 销毁struct slab对象 */  
  43. 43.                slab_destroy(cachep, slabp);  
  44. 44.            } else {  
  45. 45.                /* 将此slab添加到空slab链表中 */  
  46. 46.                list_add(&slabp->list, &l3->slabs_free);  
  47. 47.            }  
  48. 48.        } else {  
  49. 49.            /* Unconditionally move a slab to the end of the
  50. 50.             * partial list on free - maximum time for the
  51. 51.             * other objects to be freed, too.
  52. 52.             *//*将此slab添加到部分满slab链表中*/  
  53. 53.            list_add_tail(&slabp->list, &l3->slabs_partial);  
  54. 54.        }  
  55. 55.    }  
  56. 56.}  
复制代码
将对象释放到其slab中



view plaincopy to clipboard
  1. 01.static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,  
  2. 02.                void *objp, int nodeid)  
  3. 03.{   /* 获得对象在kmem_bufctl_t数组中的索引 */  
  4. 04.    unsigned int objnr = obj_to_index(cachep, slabp, objp);  
  5. 05.  
  6. 06.#if DEBUG  
  7. 07.    /* Verify that the slab belongs to the intended node */  
  8. 08.    WARN_ON(slabp->nodeid != nodeid);  
  9. 09.  
  10. 10.    if (slab_bufctl(slabp)[objnr] + 1 <= SLAB_LIMIT + 1) {  
  11. 11.        printk(KERN_ERR "slab: double free detected in cache "  
  12. 12.                "'%s', objp %p\n", cachep->name, objp);  
  13. 13.        BUG();  
  14. 14.    }  
  15. 15.#endif  
  16. 16.    /*这两步相当于静态链表的插入操作*/  
  17. 17.    /* 指向slab中原来的第一个空闲对象 */  
  18. 18.    slab_bufctl(slabp)[objnr] = slabp->free;  
  19. 19.    /* 释放的对象作为第一个空闲对象 */  
  20. 20.    slabp->free = objnr;  
  21. 21.    /* 已分配对象数减一 */  
  22. 22.    slabp->inuse--;  
  23. 23.}  
复制代码
辅助函数



view plaincopy to clipboard
  1. 01./* 通过虚拟地址得到page,再通过page得到slab */  
  2. 02.static inline struct slab *virt_to_slab(const void *obj)  
  3. 03.{  
  4. 04.    struct page *page = virt_to_head_page(obj);  
  5. 05.    return page_get_slab(page);  
  6. 06.}  
复制代码
view plaincopy to clipboard
  1. 01.static inline struct slab *page_get_slab(struct page *page)  
  2. 02.{  
  3. 03.    BUG_ON(!PageSlab(page));  
  4. 04.    return (struct slab *)page->lru.prev;  
  5. 05.}  
复制代码
可见,用page->lru.prev得到slab,和创建slab时相呼应。

作者: 芯忻相依    时间: 2012-01-10 20:01
谢谢分享




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2