您的位置:首页 > 其它

Kernel那些事儿之内存管理(8) --- Slab(中)

2015-07-16 18:10 423 查看
上篇讲了Slab中的数据结构,这篇该讲Slab中的操作了。

既然是内存管理,那操作无非就两点:allocate 和 free。

1. 申请一个object
在Slab中,申请一个object是通过函数 kmem_cache_alloc() 来完成的。
3618 void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
3619 {
3620     return __cache_alloc(cachep, flags, __builtin_return_address(0));
3621 }
该函数有两个参数:
cachep指定了要从哪个cache中申请object.

flags: 如果该cache中所有的slab都已经满了,那就不得不向buddy system求救。flags指定了此时从buddy system中申请page frame时要使用的标志。

其实函数 kmem_cache_alloc() 只是一个wapper,经过层层调用,除去必要的锁操作,最终真正干活的是函数 ____cache_alloc()。
3190 static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
3191 {
3192     void *objp;
3193     struct array_cache *ac;
3194
3195     check_irq_off();
3196
3197     ac = cpu_cache_get(cachep);
3198     if (likely(ac->avail)) {
3199         STATS_INC_ALLOCHIT(cachep);
3200         ac->touched = 1;
3201         objp = ac->entry[--ac->avail];
3202     } else {
3203         STATS_INC_ALLOCMISS(cachep);
3204         objp = cache_alloc_refill(cachep, flags);
3205     }
3206     return objp;
3207 }
该函数看起来唬人,其实其武功招式总结起来就三招,人称“分配三板斧”。

第一板斧:per-cpu object cache.
3197 ~ 3201: 首先找到当前CPU的object cache,然后检查cache中有没有可用的object,有的话就拿出一个来。然后完活收工。

定位object cache是通过以下函数完成的:
760 static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
761 {
762     return cachep->array[smp_processor_id()];
763 }


上篇讲过,object cache实际上是一个LIFO的栈结构,其中成员变量 avail 即代表了可用object的个数,又作为索引值指出了栈顶位置。
所以从object cache中拿出一个object的操作,可以通过 3201 行的语句来完成。
3201         objp = ac->entry[--ac->avail];


第二板斧:Slab中的空闲object
如果object cache中没有可用的object,那就要先填充object cache。如果不考虑 shared object cache,填充cache的object来自于Slab页面。
该操作由函数 cache_alloc_refill() 完成。
2957 static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
2958 {
2959     int batchcount;
2960     struct kmem_list3 *l3;
2961     struct array_cache *ac;
2962     int node;
2963
2964     node = numa_node_id();
2965
2966     check_irq_off();
2967     ac = cpu_cache_get(cachep);
2968 retry:
2969     batchcount = ac->batchcount;

2978     l3 = cachep->nodelists[node];
2979
2980     BUG_ON(ac->avail > 0 || !l3);
2981     spin_lock(&l3->list_lock);
2982
2983     /* See if we can refill from the shared array */
2984     if (l3->shared && transfer_objects(ac, l3->shared, batchcount))
2985         goto alloc_done;
2986
2987     while (batchcount > 0) {
2988         struct list_head *entry;
2989         struct slab *slabp;
2990         /* Get slab alloc is to come from. */
2991         entry = l3->slabs_partial.next;
2992         if (entry == &l3->slabs_partial) {
2993             l3->free_touched = 1;
2994             entry = l3->slabs_free.next;
2995             if (entry == &l3->slabs_free)
2996                 goto must_grow;
2997         }
2998
2999         slabp = list_entry(entry, struct slab, list);
3000         check_slabp(cachep, slabp);
3001         check_spinlock_acquired(cachep);
3002
3003         /*
3004          * The slab was either on partial or free list so
3005          * there must be at least one object available for
3006          * allocation.
3007          */
3008         BUG_ON(slabp->inuse < 0 || slabp->inuse >= cachep->num);
3009
3010         while (slabp->inuse < cachep->num && batchcount--) {
3011             STATS_INC_ALLOCED(cachep);
3012             STATS_INC_ACTIVE(cachep);
3013             STATS_SET_HIGH(cachep);
3014
3015             ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
3016                                 node);
3017         }
3018         check_slabp(cachep, slabp);
3019
3020         /* move slabp to correct slabp list: */
3021         list_del(&slabp->list);
3022         if (slabp->free == BUFCTL_END)
3023             list_add(&slabp->list, &l3->slabs_full);
3024         else
3025             list_add(&slabp->list, &l3->slabs_partial);
3026     }
3027
3028 must_grow:
3029     l3->free_objects -= ac->avail;
3030 alloc_done:
3031     spin_unlock(&l3->list_lock);
3032
3033     if (unlikely(!ac->avail)) {
3034         int x;
3035         x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
3036
3037         /* cache_grow can reenable interrupts, then ac could change. */
3038         ac = cpu_cache_get(cachep);
3039         if (!x && ac->avail == 0)   /* no objects in sight? abort */
3040             return NULL;
3041
3042         if (!ac->avail)     /* objects refilled by interrupt? */
3043             goto retry;
3044     }
3045     ac->touched = 1;
3046     return ac->entry[--ac->avail];
3047 }
2984行先检查一下能否从shared object cache中拿一些object填充到我们的object cache中。该操作其实就是一个memory copy的过程。

2987 ~ 3026 是从Slab页面中填充object cache的操作。
该操作首先遍历所有partial的slab,然后再遍历所有free的slab。对每个slab,拿出其中的free objects,填充到object cache中。
从slab中拿出一个free object的操作由函数 slab_get_obj() 完成。
2695 static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,
2696                 int nodeid)
2697 {
2698     void *objp = index_to_obj(cachep, slabp, slabp->free);
2699     kmem_bufctl_t next;
2700
2701     slabp->inuse++;
2702     next = slab_bufctl(slabp)[slabp->free];

2707     slabp->free = next;
2708
2709     return objp;
2710 }
我们上篇讲过,slab中所有空闲的object 的描述符组成了一个简单的链表。该函数从这个链表中取下头部的一个空闲object。

把拿出的空闲object填充到object cache的操作是由3015行完成的。
3015             ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
3016                                 node);


第三板斧:buddy system
如果所有的slab都满了,即每个slab都没有空闲object了,那怎么办?这个时候就得求助于buddy system了。
从buddy system中申请新的page frame,创建新的slab,从而创建出一批新的object。该操作由函数 cache_grow() 完成。

2760 static int cache_grow(struct kmem_cache *cachep,
2761         gfp_t flags, int nodeid, void *objp)
2762 {
2763     struct slab *slabp;
2764     size_t offset;
2765     gfp_t local_flags;
2766     struct kmem_list3 *l3;
2767
2768     /*
2769      * Be lazy and only check for valid flags here,  keeping it out of the
2770      * critical path in kmem_cache_alloc().
2771      */
2772     BUG_ON(flags & GFP_SLAB_BUG_MASK);
2773     local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
2774
2775     /* Take the l3 list lock to change the colour_next on this node */
2776     check_irq_off();
2777     l3 = cachep->nodelists[nodeid];
2778     spin_lock(&l3->list_lock);
2779
2780     /* Get colour for the slab, and cal the next value. */
2781     offset = l3->colour_next;
2782     l3->colour_next++;
2783     if (l3->colour_next >= cachep->colour)
2784         l3->colour_next = 0;
2785     spin_unlock(&l3->list_lock);
2786
2787     offset *= cachep->colour_off;
2780 ~ 2787,该函数首先确定新建slab的color值。
上篇讲过,结构体kmem_list3的成员变量colour_next,保存了下一个要使用的color值。cache描述符中的colour,保存了可用的color 数。cache描述符中的colour_off, 保存了对齐字节数。
2781 ~ 2784行把colour_next的值赋值给变量offset,然后更新colour_next的值。如果colour_next的值达到了最大值,则重置为0。
2787行用color值乘以字节对齐数,算出来的就是slab开始位置的偏移量。

确定了color值后,下一步就是要向buddy system申请page frame了。
2804     if (!objp)
2805         objp = kmem_getpages(cachep, local_flags, nodeid);
2806     if (!objp)
2807         goto failed;


为新slab申请page frame的操作由函数 kmem_getpages() 完成。
1653 static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
1654 {
1655     struct page *page;
1656     int nr_pages;
1657     int i;

1667     flags |= cachep->gfpflags;
1668     if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
1669         flags |= __GFP_RECLAIMABLE;
1670
1671     page = alloc_pages_node(nodeid, flags, cachep->gfporder);
1672     if (!page)
1673         return NULL;
1674
1675     nr_pages = (1 << cachep->gfporder);

1682     for (i = 0; i < nr_pages; i++)
1683         __SetPageSlab(page + i);
1684     return page_address(page);
1685 }
函数 kmem_getpages() 首先确定分配flags。至此,调用 kmem_cache_alloc() 时传递的参数 flags 终于被用到了。但是这个flags还要再加上cache本身的flag,才会被用来申请page frame。
然后1671行调用了我们前面讲的alloc_pages函数,从buddy system中申请page frame。申请的page frame order为cachep->gfporder。
1682 ~ 1683行,为每个申请到的page frame的描述符设置 PG_slab 标志。
最后返回第一个page frame的线性地址。

好,申请到page frame了,那下一步做什么?让我们回到函数 cache_grow() 继续跟踪。
2810     slabp = alloc_slabmgmt(cachep, objp, offset,
2811             local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
2812     if (!slabp)
2813         goto opps1;
2810 ~ 2813为新建的slab 分配并初始化slab描述符,由函数 alloc_slabmgmt() 完成。
2609 static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
2610                    int colour_off, gfp_t local_flags,
2611                    int nodeid)
2612 {
2613     struct slab *slabp;
2614
2615     if (OFF_SLAB(cachep)) {
2616         /* Slab management obj is off-slab. */
2617         slabp = kmem_cache_alloc_node(cachep->slabp_cache,
2618                           local_flags & ~GFP_THISNODE, nodeid);
2619         if (!slabp)
2620             return NULL;
2621     } else {
2622         slabp = objp + colour_off;
2623         colour_off += cachep->slab_size;
2624     }
2625     slabp->inuse = 0;
2626     slabp->colouroff = colour_off;
2627     slabp->s_mem = objp + colour_off;
2628     slabp->nodeid = nodeid;
2629     return slabp;
2630 }
2615 ~ 2620:针对external slab,从cachep->slabp_cache中分配slab描述符(以及object描述符)。
2622 ~ 2623:针对internal slab,在slab页面内指定slab描述符的地址。考虑进去slab coloring, slab描述符放在 (color*aln) 位置处。

2625 ~ 2628:初始化新建的slab描述符。正如上篇讲到的,第一个object的偏移量定义为(color * aln) + dsize。

有了slab描述符后,让我们回到函数 cache_grow() 继续跟踪。
2816     slab_map_pages(cachep, slabp, objp);
2817
2818     cache_init_objs(cachep, slabp);
2819


2816行建立page frame与cache和slab描述符之间的关联。

2737 static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
2738                void *addr)
2739 {
2740     int nr_pages;
2741     struct page *page;
2742
2743     page = virt_to_page(addr);
2744
2745     nr_pages = 1;
2746     if (likely(!PageCompound(page)))
2747         nr_pages <<= cache->gfporder;
2748
2749     do {
2750         page_set_cache(page, cache);
2751         page_set_slab(page, slab);
2752         page++;
2753     } while (--nr_pages);
2754 }
建立关联的方式就是重用了page描述符中的成员变量lru。
583 static inline void page_set_cache(struct page *page, struct kmem_cache *cache)
584 {
585     page->lru.next = (struct list_head *)cache;
586 }


595 static inline void page_set_slab(struct page *page, struct slab *slab)
596 {
597     page->lru.prev = (struct list_head *)slab;
598 }


2818行初始化新建slab中的objects.
2637 static void cache_init_objs(struct kmem_cache *cachep,
2638                 struct slab *slabp)
2639 {
2640     int i;
2641
2642     for (i = 0; i < cachep->num; i++) {
2643         void *objp = index_to_obj(cachep, slabp, i);

2676         if (cachep->ctor)
2677             cachep->ctor(cachep, objp);

2679         slab_bufctl(slabp)[i] = i + 1;
2680     }
2681     slab_bufctl(slabp)[i - 1] = BUFCTL_END;
2682     slabp->free = 0;
2683 }
这个初始化做了两件事:
如果该cache定义了构造函数,则对每个新建的object执行构造函数。

初始化object描述符,构造出空闲object链表。

建立了关联,初始化了object,继续跟踪 cache_grow()。
2823     spin_lock(&l3->list_lock);
2824
2825     /* Make slab active. */
2826     list_add_tail(&slabp->list, &(l3->slabs_free));
2827     STATS_INC_GROWN(cachep);
2828     l3->free_objects += cachep->num;
2829     spin_unlock(&l3->list_lock);
2830     return 1;
2831 opps1:
2832     kmem_freepages(cachep, objp);
2833 failed:
2834     if (local_flags & __GFP_WAIT)
2835         local_irq_disable();
2836     return 0;
2837 }
把新建的slab添加到cache的free slab链表中,更新kmem_list3中free_objects的值,然后完活收工。

第三板斧使出来之后,该cache新增了一slab的free object。于是重新使用第二板斧。

2. 释放一个object
释放一个object的API是函数 kmem_cache_free().
3760 void kmem_cache_free(struct kmem_cache *cachep, void *objp)
3761 {
3762     unsigned long flags;
3763
3764     local_irq_save(flags);
3765     debug_check_no_locks_freed(objp, obj_size(cachep));
3766     __cache_free(cachep, objp);
3767     local_irq_restore(flags);
3768 }


该函数也是一个wrapper,最终干活的是函数 __cache_free()。
3582 static inline void __cache_free(struct kmem_cache *cachep, void *objp)
3583 {
3584     struct array_cache *ac = cpu_cache_get(cachep);

3598
3599     if (likely(ac->avail < ac->limit)) {
3600         STATS_INC_FREEHIT(cachep);
3601         ac->entry[ac->avail++] = objp;
3602         return;
3603     } else {
3604         STATS_INC_FREEMISS(cachep);
3605         cache_flusharray(cachep, ac);
3606         ac->entry[ac->avail++] = objp;
3607     }
3608 }
相对应的,该函数也是三板斧,人称“释放三板斧”,而且这三板斧与“申请三板斧”一一对应,真是一对生死冤家。

第一板斧:per-cpu object cache
3599 ~ 3602,如果当前CPU的object cache没有满,则直接把要释放的object放到object cache中,然后完活收工。

第二板斧:Slab页面
如果object cache满了,则要刷一批object到slab页面中。该操作由函数 cache_flusharray() 完成。
3527 static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
3528 {
3529     int batchcount;
3530     struct kmem_list3 *l3;
3531     int node = numa_node_id();
3532
3533     batchcount = ac->batchcount;

3537     check_irq_off();
3538     l3 = cachep->nodelists[node];
3539     spin_lock(&l3->list_lock);
3540     if (l3->shared) {
3541         struct array_cache *shared_array = l3->shared;
3542         int max = shared_array->limit - shared_array->avail;
3543         if (max) {
3544             if (batchcount > max)
3545                 batchcount = max;
3546             memcpy(&(shared_array->entry[shared_array->avail]),
3547                    ac->entry, sizeof(void *) * batchcount);
3548             shared_array->avail += batchcount;
3549             goto free_done;
3550         }
3551     }
3552
3553     free_block(cachep, ac->entry, batchcount, node);
3554 free_done:

3573     spin_unlock(&l3->list_lock);
3574     ac->avail -= batchcount;
3575     memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
3576 }
3540 ~ 3551:如果shared object cache没有满额,则转移一批object到shared cache中。

3553行:利用函数 free_block() 刷一批object到slab页面中。
3575行:把object cache中剩下的object上移。为了保证object cache是LIFO的结构,会把最早放入object cache的那些object刷掉,因此需要把剩下的object向上移动。

3483 static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
3484                int node)
3485 {
3486     int i;
3487     struct kmem_list3 *l3;
3488
3489     for (i = 0; i < nr_objects; i++) {
3490         void *objp = objpp[i];
3491         struct slab *slabp;
3492
3493         slabp = virt_to_slab(objp);
3494         l3 = cachep->nodelists[node];
3495         list_del(&slabp->list);
3496         check_spinlock_acquired_node(cachep, node);
3497         check_slabp(cachep, slabp);
3498         slab_put_obj(cachep, slabp, objp, node);
3499         STATS_DEC_ACTIVE(cachep);
3500         l3->free_objects++;
3501         check_slabp(cachep, slabp);
3502
3503         /* fixup slab chains */
3504         if (slabp->inuse == 0) {
3505             if (l3->free_objects > l3->free_limit) {
3506                 l3->free_objects -= cachep->num;
3507                 /* No need to drop any previously held
3508                  * lock here, even if we have a off-slab slab
3509                  * descriptor it is guaranteed to come from
3510                  * a different cache, refer to comments before
3511                  * alloc_slabmgmt.
3512                  */
3513                 slab_destroy(cachep, slabp);
3514             } else {
3515                 list_add(&slabp->list, &l3->slabs_free);
3516             }
3517         } else {
3518             /* Unconditionally move a slab to the end of the
3519              * partial list on free - maximum time for the
3520              * other objects to be freed, too.
3521              */
3522             list_add_tail(&slabp->list, &l3->slabs_partial);
3523         }
3524     }
3525 }


3493行根据object的地址,确定出slab描述符的地址。这里就用到我们在创建slab时建立的关联了。

612 static inline struct slab *virt_to_slab(const void *obj)
613 {
614     struct page *page = virt_to_head_page(obj);
615     return page_get_slab(page);
616 }
600 static inline struct slab *page_get_slab(struct page *page)
601 {
602     BUG_ON(!PageSlab(page));
603     return (struct slab *)page->lru.prev;
604 }


3498行把一个object放回到slab中。
2712 static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
2713                 void *objp, int nodeid)
2714 {
2715     unsigned int objnr = obj_to_index(cachep, slabp, objp);

2727     slab_bufctl(slabp)[objnr] = slabp->free;
2728     slabp->free = objnr;
2729     slabp->inuse--;
2730 }


3504 ~ 3523:放回一个object后,就要检查该slab的状态:
partial:把slab挂到slabs_partial链表上。

free:检查cache中free object的数量有没有超过限制。
没有超过:把slab挂到slabs_free链表中。

超过了:删掉该slab,把其page frame还给buddy system。

第三板斧:buddy system

删除一个slab的操作是由函数 slab_destroy() 完成的。
1944 static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp)
1945 {
1946     void *addr = slabp->s_mem - slabp->colouroff;
1947

1957     kmem_freepages(cachep, addr);
1958     if (OFF_SLAB(cachep))
1959         kmem_cache_free(cachep->slabp_cache, slabp);

1961 }
该函数首先释放slab所用的page frame,然后对于external slab,把slab描述符占用的空间释放掉。

释放slab所用page frame的操作是由函数 kmem_freepages() 完成的。
1690 static void kmem_freepages(struct kmem_cache *cachep, void *addr)
1691 {
1692     unsigned long i = (1 << cachep->gfporder);
1693     struct page *page = virt_to_page(addr);

1702     while (i--) {
1703         BUG_ON(!PageSlab(page));
1704         __ClearPageSlab(page);
1705         page++;
1706     }

1709     free_pages((unsigned long)addr, cachep->gfporder);
1710 }
1702 ~ 1706:对slab所用的每个page frame,清除其描述符中的 PG_slab 标志。
1709:调用函数 free_pages() 把slab所用的page frame还给buddy system。

本文出自 “内核部落格” 博客,请务必保留此出处http://richardguo.blog.51cto.com/9343720/1675411
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: