linux 3.4.10 内核内存管理源代码分析9:Slab内存释放

法律声明linux 3.4.10 内核内存管理源代码分析》系列文章由机器人[email protected])发表于http://blog.csdn.net/ancjf,文章遵循GPL协议。欢迎转载,转载请注明作者和此条款。


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中。

猜你喜欢

转载自blog.csdn.net/ancjf/article/details/9067481