感谢支持
我们一直在努力

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并将它添加到三链的空闲链表中。


主实现


  1. /* 

  2.  * Release an obj back to its cache. If the obj has a constructed state, it must 

  3.  * be in this state _before_ it is released.  Called with disabled ints. 

  4.  */  

  5. static inline void __cache_free(struct kmem_cache *cachep, void *objp)  

  6. {  

  7.     /* 获得本CPU的local cache */  

  8.     struct array_cache *ac = cpu_cache_get(cachep);  

  9.   

  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[ac->avail++] = objp;  

  31.         return;  

  32.     } else {  

  33.         /* 大于等于上限时, */  

  34.         STATS_INC_FREEMISS(cachep);  

  35.         /* local cache中对象过多,需要释放一批对象到slab三链中。*/  

  36.         cache_flusharray(cachep, ac);  

  37.         ac->entry[ac->avail++] = objp;  

  38.     }  

  39. }  

释放对象到三链中


  1. /*local cache中对象过多,需要释放一批对象到slab三链中。*/  

  2. static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)  

  3. {  

  4.     int batchcount;  

  5.     struct kmem_list3 *l3;  

  6.     int node = numa_node_id();  

  7.     /* 每次释放多少个对象 */  

  8.     batchcount = ac->batchcount;  

  9. #if DEBUG   

  10.     BUG_ON(!batchcount || batchcount > ac->avail);  

  11. #endif   

  12.     check_irq_off();  

  13.     /* 获得此cache的slab三链 */  

  14.     l3 = cachep->nodelists[node];  

  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[shared_array->avail]),  

  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[batchcount]), sizeof(void *)*ac->avail);  

  60. }  

shared local cache,释放对象到slab三链中


  1. /* 

  2.  * Caller needs to acquire correct kmem_list’s list_lock 

  3.  */  

  4.  /*释放一定数目的对象*/  

  5. static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,  

  6.                int node)  

  7. {  

  8.     int i;  

  9.     struct kmem_list3 *l3;  

  10.      /* 逐一释放对象到slab三链中 */  

  11.     for (i = 0; i < nr_objects; i++) {  

  12.         void *objp = objpp[i];  

  13.         struct slab *slabp;  

  14.         /* 通过虚拟地址得到page,再通过page得到slab */  

  15.         slabp = virt_to_slab(objp);  

  16.         /* 获得slab三链 */  

  17.         l3 = cachep->nodelists[node];  

  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


  1. static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,  

  2.                 void *objp, int nodeid)  

  3. {   /* 获得对象在kmem_bufctl_t数组中的索引 */  

  4.     unsigned int objnr = obj_to_index(cachep, slabp, objp);  

  5.   

  6. #if DEBUG   

  7.     /* Verify that the slab belongs to the intended node */  

  8.     WARN_ON(slabp->nodeid != nodeid);  

  9.   

  10.     if (slab_bufctl(slabp)[objnr] + 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)[objnr] = slabp->free;  

  19.     /* 释放的对象作为第一个空闲对象 */  

  20.     slabp->free = objnr;  

  21.     /* 已分配对象数减一 */  

  22.     slabp->inuse–;  

  23. }  

辅助函数


  1. /* 通过虚拟地址得到page,再通过page得到slab */  

  2. static inline struct slab *virt_to_slab(const void *obj)  

  3. {  

  4.     struct page *page = virt_to_head_page(obj);  

  5.     return page_get_slab(page);  

  6. }  

 


  1. static inline struct slab *page_get_slab(struct page *page)  

  2. {  

  3.     BUG_ON(!PageSlab(page));  

  4.     return (struct slab *)page->lru.prev;  

  5. }  

可见,用page->lru.prev得到slab,和创建slab时相呼应。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux内存管理之slab机制(释放对象)
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏