從一個緩存中分配對象總是遵循下面的原則:
1.本地高速緩存中是否有空閒對象,如果有的話則從其中獲取對象,這時分配的對象是最“熱”的;
2.如果本地高速緩存中沒有對象,則從kmem_list3中的slab鏈表中尋找空閒對象並填充到本地高速緩存再分配;
3.如果所有的slab中都沒有空閒對象了,那麼就要創建新的slab,再分配 。
函數kmem_cache_alloc用於從特定的緩存獲取對象,kmalloc用於從普通緩存中獲取對象,它們的執行流程如下圖所示
實質性的工作是從____cache_alloc()開始的,因此從這個函數作為入口來分析
- static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
- {
- void *objp;
- struct array_cache *ac;
-
- check_irq_off();
-
- /*獲取緩存的本地高速緩存的描述符array_cache*/
- ac = cpu_cache_get(cachep);
-
- /*如果本地高速緩存中還有空閒對象可以分配則從本地高速緩存中分配*/
- if (likely(ac->avail)) {
- STATS_INC_ALLOCHIT(cachep);
- ac->touched = 1;
- /*先將avail的值減1,這樣avail對應的空閒對象是最熱的,即最近釋放出來的,
- 更有可能駐留在CPU高速緩存中*/
- objp = ac->entry[--ac->avail];
- } else {/*否則需要填充本地高速緩存*/
- STATS_INC_ALLOCMISS(cachep);
- objp = cache_alloc_refill(cachep, flags);
- }
- /*
- * To avoid a false negative, if an object that is in one of the
- * per-CPU caches is leaked, we need to make sure kmemleak doesn't
- * treat the array pointers as a reference to the object.
- */
- kmemleak_erase(&ac->entry[ac->avail]);
- return objp;
- }
- static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
- {
- int batchcount;
- struct kmem_list3 *l3;
- struct array_cache *ac;
- int node;
-
- retry:
- check_irq_off();
- node = numa_node_id();
- ac = cpu_cache_get(cachep);
- batchcount = ac->batchcount; /*獲取批量轉移的數目*/
- if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
- /*
- * If there was little recent activity on this cache, then
- * perform only a partial refill. Otherwise we could generate
- * refill bouncing.
- */
- batchcount = BATCHREFILL_LIMIT;
- }
- /*獲取kmem_list3*/
- l3 = cachep->nodelists[node];
-
- BUG_ON(ac->avail > 0 || !l3);
- spin_lock(&l3->list_lock);
-
- /* See if we can refill from the shared array */
- /*如果有共享本地高速緩存,則從共享本地高速緩存填充*/
- if (l3->shared && transfer_objects(ac, l3->shared, batchcount))
- goto alloc_done;
-
- while (batchcount > 0) {
- struct list_head *entry;
- struct slab *slabp;
- /* Get slab alloc is to come from. */
- /*掃描slab鏈表,先從partial鏈表開始,如果整個partial鏈表都無法找到batchcount個空閒對象,
- 再掃描free鏈表*/
- entry = l3->slabs_partial.next;
-
- /*entry回到表頭說明partial鏈表已經掃描完畢,開始掃描free鏈表*/
- if (entry == &l3->slabs_partial) {
- l3->free_touched = 1;
- entry = l3->slabs_free.next;
- if (entry == &l3->slabs_free)
- goto must_grow;
- }
-
- /*由鏈表項得到slab描述符*/
- slabp = list_entry(entry, struct slab, list);
- check_slabp(cachep, slabp);
- check_spinlock_acquired(cachep);
-
- /*
- * The slab was either on partial or free list so
- * there must be at least one object available for
- * allocation.
- */
- BUG_ON(slabp->inuse >= cachep->num);
-
- /*如果slabp中還存在空閒對象並且還需要繼續填充對象到本地高速緩存*/
- while (slabp->inuse < cachep->num && batchcount--) {
- STATS_INC_ALLOCED(cachep);
- STATS_INC_ACTIVE(cachep);
- STATS_SET_HIGH(cachep);
-
- /*填充的本質就是用ac後面的void*數組元素指向一個空閒對象*/
- ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
- node);
- }
- check_slabp(cachep, slabp);
-
- /* move slabp to correct slabp list: */
- /*由於從slab中分配出去了對象,因此有可能需要將slab移到其他鏈表中去*/
- list_del(&slabp->list);
- /*free等於BUFCTL_END表示空閒對象已耗盡,將slab插入full鏈表*/
- if (slabp->free == BUFCTL_END)
- list_add(&slabp->list, &l3->slabs_full);
- else/*否則肯定是插入partial鏈表*/
- list_add(&slabp->list, &l3->slabs_partial);
- }
-
- must_grow:
- l3->free_objects -= ac->avail;/*刷新kmem_list3中的空閒對象*/
- alloc_done:
- spin_unlock(&l3->list_lock);
-
- /*avail為0表示kmem_list3中的slab全部處於full狀態或者沒有slab,則要為緩存分配slab*/
- if (unlikely(!ac->avail)) {
- int x;
- x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
-
- /* cache_grow can reenable interrupts, then ac could change. */
- ac = cpu_cache_get(cachep);
- if (!x && ac->avail == 0) /* no objects in sight? abort */
- return NULL;
-
- if (!ac->avail) /* objects refilled by interrupt? */
- goto retry;
- }
- ac->touched = 1;
- /*返回最後一個末端的對象*/
- return ac->entry[--ac->avail];
- }
對於所有slab都空閒對象的情況,需要調用cache_grow()來增加cache的容量,這個函數在後面分析slab的分配時再做介紹。
相關閱讀:
Linux Slab分配器(一)--概述 http://www.linuxidc.com/Linux/2012-06/62965.htm
Linux Slab分配器(二)--初始化 http://www.linuxidc.com/Linux/2012-06/62966.htm
Linux Slab分配器(三)--創建緩存 http://www.linuxidc.com/Linux/2012-06/63109.htm
Linux Slab分配器(五)--釋放對象 http://www.linuxidc.com/Linux/2012-06/63167.htm