Linux內核中創建slab主要由函數cache_grow()實現,從slab的創建中我們可以完整地看到slab與對象、頁面的組織方式。
- /*
- * Grow (by 1) the number of slabs within a cache. This is called by
- * kmem_cache_alloc() when there are no active objs left in a cache.
- */
- /*使用一個或多個頁面創建一個空slab。
- objp:頁面虛擬地址,為空表示還未申請內存頁,不為空
- ,說明已申請內存頁,可直接用來創建slab*/
- static int cache_grow(struct kmem_cache *cachep,
- gfp_t flags, int nodeid, void *objp)
- {
- struct slab *slabp;
- size_t offset;
- gfp_t local_flags;
- struct kmem_list3 *l3;
-
- /*
- * Be lazy and only check for valid flags here, keeping it out of the
- * critical path in kmem_cache_alloc().
- */
- BUG_ON(flags & GFP_SLAB_BUG_MASK);
- local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
-
- /* Take the l3 list lock to change the colour_next on this node */
- check_irq_off();
- /* 獲得本內存節點的slab三鏈 */
- l3 = cachep->nodelists[nodeid];
- spin_lock(&l3->list_lock);
-
- /* Get colour for the slab, and cal the next value. */
- /* 獲得本slab的著色區偏移 */
- offset = l3->colour_next;
- /* 更新著色區偏移,使不同slab的著色偏移不同 */
- l3->colour_next++;
- /* 不能超過著色區的總大小,如果超過了,重置為0。這就是前面分析過的著色循環問題
- 。事實上,如果slab中浪費的空間很少,那麼很快就會循環一次。*/
- if (l3->colour_next >= cachep->colour)
- l3->colour_next = 0;
- spin_unlock(&l3->list_lock);
- /* 將著色單位區間的個數轉換為著色區大小 */
- offset *= cachep->colour_off;
-
- if (local_flags & __GFP_WAIT)
- local_irq_enable();
-
- /*
- * The test for missing atomic flag is performed here, rather than
- * the more obvious place, simply to reduce the critical path length
- * in kmem_cache_alloc(). If a caller is seriously mis-behaving they
- * will eventually be caught here (where it matters).
- */
- kmem_flagcheck(cachep, flags);
-
- /*
- * Get mem for the objs. Attempt to allocate a physical page from
- * 'nodeid'.
- */
- if (!objp)/* 還未分配頁面,從本內存節點分配1<<cachep->gfporder個頁面
- ,objp為slab首頁面的虛擬地址 */
- objp = kmem_getpages(cachep, local_flags, nodeid);
- if (!objp)
- goto failed;
-
- /* Get slab management. */
- /* 分配slab管理對象 */
- slabp = alloc_slabmgmt(cachep, objp, offset,
- local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
- if (!slabp)
- goto opps1;
- /* 設置page到cache、slab的映射 */
- slab_map_pages(cachep, slabp, objp);
-
- /* 初始化slab中的對象 */
- cache_init_objs(cachep, slabp);
-
- if (local_flags & __GFP_WAIT)
- local_irq_disable();
- check_irq_off();
- spin_lock(&l3->list_lock);
-
- /* Make slab active. */
- list_add_tail(&slabp->list, &(l3->slabs_free));
- /* 更新本cache增長計數 */
- STATS_INC_GROWN(cachep);
- /* 更新slab鏈表中空閒對象計數 */
- l3->free_objects += cachep->num;
- spin_unlock(&l3->list_lock);
- return 1;
- opps1:
- kmem_freepages(cachep, objp);
- failed:
- if (local_flags & __GFP_WAIT)
- local_irq_disable();
- return 0;
- }
執行流程:
1,從cache結構中獲得並計算著色區偏移量;
2,從伙伴系統中獲得1<<cachep->gfporder個頁面用於slab;
3,初始化slab中相關變量,如果是外置式slab需要從新申請slab管理區的空間,由函數alloc_slabmgmt()實現。
- /*分配slab管理對象*/
- static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
- int colour_off, gfp_t local_flags,
- int nodeid)
- {
- struct slab *slabp;
-
- if (OFF_SLAB(cachep)) {
- /* Slab management obj is off-slab. */
- /* 外置式slab。從general slab cache中分配一個管理對象,
- slabp_cache指向保存有struct slab對象的general slab cache。
- slab初始化階段general slab cache可能還未創建,slabp_cache指針為空
- ,故初始化階段創建的slab均為內置式slab。*/
- slabp = kmem_cache_alloc_node(cachep->slabp_cache,
- local_flags, nodeid);
- /*
- * If the first object in the slab is leaked (it's allocated
- * but no one has a reference to it), we want to make sure
- * kmemleak does not treat the ->s_mem pointer as a reference
- * to the object. Otherwise we will not report the leak.
- *//* 對第一個對象做檢查 */
- kmemleak_scan_area(slabp, offsetof(struct slab, list),
- sizeof(struct list_head), local_flags);
- if (!slabp)
- return NULL;
- } else {/* 內置式slab。objp為slab首頁面的虛擬地址,加上著色偏移
- ,得到slab管理對象的虛擬地址 */
- slabp = objp + colour_off;
- /* 計算slab中第一個對象的頁內偏移,slab_size保存slab管理對象的大小
- ,包含struct slab對象和kmem_bufctl_t數組 */
- colour_off += cachep->slab_size;
- } /* 在用(已分配)對象數為0 */
- slabp->inuse = 0;
- /* 第一個對象的頁內偏移,可見對於內置式slab,colouroff成員不僅包括著色區
- ,還包括管理對象占用的空間
- ,外置式slab,colouroff成員只包括著色區。*/
- slabp->colouroff = colour_off;
- /* 第一個對象的虛擬地址 */
- slabp->s_mem = objp + colour_off;
- /* 內存節點ID */
- slabp->nodeid = nodeid;
- /* 第一個空閒對象索引為0,即kmem_bufctl_t數組的第一個元素 */
- slabp->free = 0;
- return slabp;
- }