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 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 memoryreference) 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时相呼应。

芯忻相依 发表于 2012-01-10 20:01

谢谢分享
页: [1]
查看完整版本: Linux内存管理之slab机制(释放对象)