linux 源代码分析9:Slab内存释放

  1. Slab内存释放
    1. kmem_cache_free函数
    2. __cache_free函数
    3. free_block函数
    4. slab_destroy函数
    5. kmem_freepages函数

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" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏