linux 源代码分析9:Slab内存释放
Slab内存释放
kmem_cache_free函数
向一个slab中释放内存的的函数是kmem_cache_free,在mm/slab.c中实现,代码如下:
3914 void kmem_cache_free(struct kmem_cache*cachep, void *objp)
3915 {
3916 unsigned long flags;
3917
3918 local_irq_save(flags);
3919 debug_check_no_locks_freed(objp, obj_size(cachep));
3920 if (!(cachep->flags & SLAB_DEBUG_OBJECTS))
3921 debug_check_no_obj_freed(objp,obj_size(cachep));
3922 __cache_free(cachep, objp, __builtin_return_address(0));
3923 local_irq_restore(flags);
3924
3925 trace_kmem_cache_free(_RET_IP_, objp);
3926 }
除了开关中断和一些调试代码,释放的主要工作是调用__cache_free函数实现的。
__cache_free函数
释放也分两种情况,如果一个cpu对象缓存的对象数目达到了上限,slab系统会对cup对象缓存进行刷新,就是把对象归还给slab块,如果这样造成的slab空闲对象数也超过限制,则会把空闲slab块也释放回伙伴系统。__cache_free在mm/slab.c中实现,代码如下:
3670 static void cache_flusharray(structkmem_cache *cachep, struct array_cache *ac)
3671 {
3672 int batchcount;
3673 struct kmem_list3 *l3;
3674 int node = numa_mem_id();
3675
3676 batchcount = ac->batchcount;
3677 #if DEBUG
3678 BUG_ON(!batchcount || batchcount > ac->avail);
3679 #endif
3680 check_irq_off();
3681 l3 = cachep->nodelists[node];
3682 spin_lock(&l3->list_lock);
3683 if (l3->shared) {
3684 struct array_cache*shared_array = l3->shared;
3685 int max =shared_array->limit - shared_array->avail;
3686 if (max) {
3687 if (batchcount >max)
3688 batchcount =max;
3689 memcpy(&(shared_array->entry[shared_array->avail]),
3690 ac->entry, sizeof(void *) *batchcount);
3691 shared_array->avail+= batchcount;
3692 goto free_done;
3693 }
3694 }
3695
3696 free_block(cachep, ac->entry, batchcount, node);
3697 free_done:
3698 #if STATS
3699 {
3700 int i = 0;
3701 struct list_head *p;
3702
3703 p = l3->slabs_free.next;
3704 while (p !=&(l3->slabs_free)) {
3705 struct slab *slabp;
3706
3707 slabp = list_entry(p,struct slab, list);
3708 BUG_ON(slabp->inuse);
3709
3710 i++;
3711 p = p->next;
3712 }
3713 STATS_SET_FREEABLE(cachep, i);
3714 }
3715 #endif
3716 spin_unlock(&l3->list_lock);
3717 ac->avail -= batchcount;
3718 memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void*)*ac->avail);
3719 }
不考虑宏DEBUG和宏STATS中的代码,cache_flusharray函数就分成两种情况,一种是本节点的三链表存在共享slab对象缓存的情况,这样情况会把slab对象移动到三链表中的共享的slab对象缓存中,否则会调用free_block函数清理slab对象缓存中的队列。
3683-3694行是三链表执行的共享slab缓存对象不为空的情况,这中情况会把slab对象缓存中超过限制的对象移动到共享的slab缓存对象中。3685行求出共享的slab对象缓存还能存放多少个对象,如果还能往里面放入对象就执行3687-3692行代码,把一些对象移动到共享slab对象缓存中。
3696调用free_block函数释放对象。
3717行减少可用对象计数
3718行因为前面的对象已经被移动到共享的slab对象缓存或释放给对象所属的slab块了,但后面还有对象是可用的,所以要把后面对象的指针前移。
free_block函数
真正把对象释放回slab块是在free_block中完成的,free_block在mm/slab.c中实现,代码如下:
3626 static void free_block(struct kmem_cache*cachep, void **objpp, int nr_objects,
3627 int node)
3628 {
3629 int i;
3630 struct kmem_list3 *l3;
3631
3632 for (i = 0; i < nr_objects; i++) {
3633 void *objp = objpp[i];
3634 struct slab *slabp;
3635
3636 slabp = virt_to_slab(objp);
3637 l3 =cachep->nodelists[node];
3638 list_del(&slabp->list);
3639 check_spinlock_acquired_node(cachep, node);
3640 check_slabp(cachep,slabp);
3641 slab_put_obj(cachep, slabp,objp, node);
3642 STATS_DEC_ACTIVE(cachep);
3643 l3->free_objects++;
3644 check_slabp(cachep, slabp);
3645
3646 /* fixup slab chains*/
3647 if (slabp->inuse == 0) {
3648 if(l3->free_objects > l3->free_limit) {
3649 l3->free_objects -= cachep->num;
3650 /* No need to drop any previously held
3651 * lock here,even if we have a off-slab slab
3652 * descriptorit is guaranteed to come from
3653 * a differentcache, refer to comments before
3654 *alloc_slabmgmt.
3655 */
3656 slab_destroy(cachep, slabp);
3657 } else {
3658 list_add(&slabp->list,&l3->slabs_free);
3659 }
3660 } else {
3661 /* Unconditionallymove a slab to the end of the
3662 * partial list onfree - maximum time for the
3663 * other objects to befreed, too.
3664 */
3665 list_add_tail(&slabp->list, &l3->slabs_partial);
3666 }
3667 }
3668 }
free_block的代码其实也比较简单,下面分析几行关键代码。
3636行调用virt_to_slab获得struct slab的地址,我们知道根据虚拟地址可以获得管理该虚拟你在的struct page结构地址,而structslab地址存储了struct page的成员lru.prev中。3641行调用slab_put_obj函数把对象归还给salb块。
3638行把slab块从原来的链表删除,因为向该slab块归还了一个对象,该slab块可能已经成为完全空闲块,3647根据slab块是否有对象正在使用中来把slab块加入不同的队列,3649-3659是slab块完全空闲的情况。如果slab块完全空闲,还要考虑一种情况就是空闲slab块的数目是不是超过了限制,3649-3656属于这种情况,3649减少空闲对象计数,3656调用slab_destroy块释放一块slab块。对于空闲块数量没有超过限制的情况,3658把slab块加入三链表的完全空闲队列。对还不是完全空闲的slab块,3665行吧slab块加入部分空闲链表。
slab_destroy函数
slab_destroy用于销毁slab块,就是把slab块释放到伙伴系统,在mm/slab.c中实现,代码如下:
2085 static void slab_destroy(structkmem_cache *cachep, struct slab *slabp)
2086 {
2087 void *addr = slabp->s_mem - slabp->colouroff;
2088
2089 slab_destroy_debugcheck(cachep, slabp);
2090 if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) {
2091 struct slab_rcu *slab_rcu;
2092
2093 slab_rcu = (struct slab_rcu*)slabp;
2094 slab_rcu->cachep = cachep;
2095 slab_rcu->addr = addr;
2096 call_rcu(&slab_rcu->head,kmem_rcu_free);
2097 } else {
2098 kmem_freepages(cachep, addr);
2099 if (OFF_SLAB(cachep))
2100 kmem_cache_free(cachep->slabp_cache, slabp);
2101 }
2102 }
在函数alloc_slabmgmt计算struct slab的存放地址是用2798行代码: slabp->s_mem = objp + colour_off,现在计算slab块的首地址就是slabp->s_mem -slabp->colouroff。
不考虑使用rcu的情况,剩下的代码就是2098行调用kmem_freepages释放slab的内存,2099-2100如果struct slab是单独存放的,也需要释放这部分空间,这部分空间是属于slab的,调用kmem_cache_free释放。
kmem_freepages函数
kmem_freepages在mm/slab.c中实现,代码如下:
1837 static void kmem_freepages(structkmem_cache *cachep, void *addr)
1838 {
1839 unsigned long i = (1 << cachep->gfporder);
1840 struct page *page = virt_to_page(addr);
1841 const unsigned long nr_freed = i;
1842
1843 kmemcheck_free_shadow(page, cachep->gfporder);
1844
1845 if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
1846 sub_zone_page_state(page_zone(page),
1847 NR_SLAB_RECLAIMABLE,nr_freed);
1848 else
1849 sub_zone_page_state(page_zone(page),
1850 NR_SLAB_UNRECLAIMABLE, nr_freed);
1851 while (i--) {
1852 BUG_ON(!PageSlab(page));
1853 __ClearPageSlab(page);
1854 page++;
1855 }
1856 if (current->reclaim_state)
1857 current->reclaim_state->reclaimed_slab += nr_freed;
1858 free_pages((unsigned long)addr, cachep->gfporder);
1859 }
kmem_freepages函数调用free_pages释放slab块的空间到伙伴系统,free_pages需要两个参数:地址和slab块的阶,slab块的阶存放在struct kmem_cache的成员gfporder中。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 ancjf@163.com
文章标题:linux 源代码分析9:Slab内存释放
本文作者:ancjf
发布时间:2013-06-09, 20:17:42
最后更新:2020-04-18, 15:57:56
原始链接:http://ancjf.com/2013/06/09/linux-3-4-10-%E5%86%85%E6%A0%B8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%909-Slab%E5%86%85%E5%AD%98%E9%87%8A%E6%94%BE/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。