相關源碼:
mm/slab.c
slab分配器描述符創建好後,接下來要做的就是系統是怎樣分配這些slab對象的?slab對象的分配函數為kmem_cache_alloc,不過實際的分配在slab_alloc函數中處理看下它的實現:
kmem_cache_alloc->slab_alloc函數
![內存管理(20)分配slab對象](http://p2.ttnews.xyz/loading.gif)
slab_alloc實現1
![內存管理(20)分配slab對象](http://p2.ttnews.xyz/loading.gif)
slab_alloc實現2
- 關閉本地中斷,調用__do_cache_alloc函數來分配slab對象,其實現細節如下↓
__do_cache_alloc函數
__do_cache_alloc實現
- 第53行:調用____cache_alloc函數來分配slab對象
- 第60行:如果slab對象分配失敗,則使用____cache_alloc_node從其他內存節點分配slab對象
____cache_alloc函數
____cache_alloc實現
- 獲取cachep描述符的本地對象緩衝池
- 本地對象緩衝池剛創建的時候avail為0,見 所以執行後面的步驟
- 本地對象緩衝池沒有可用的對象,調用cache_alloc_refill函數向本地對象緩衝池填充對象。其實現細節如下↓
cache_alloc_refill函數
cache_alloc_refill實現1
- 第97行:獲取本地內存節點ID
- 第101行:獲取本地對象緩衝池地址
- 第106行:獲取內存節點對應的slab節點
- 第110~112行:共享對象緩衝池是否有空閒對象,如果有則將空閒對象填充到本地對象緩衝池
cache_alloc_refill實現2
- 第118~124行:在slab‘對象部分空閒’鏈表中查找是否有空閒對象沒有則到slab‘對象全部空閒’看有沒有,如果沒有跳轉到must_grow位置;否則就返回有空閒對象的slab節點。
- 第125行:獲取到有空閒對象slab節點,就可以通過list_entry找到slab節點所在page首地址
- 第128~134行:page->active表示當前頁中freelist數組下標的索引,而freelist數組的內容表示的則是slab中對象的索引(active相當於表示當前slab還有的空閒對象是否已耗盡,當active等於num時就表示空閒對象耗盡了)。所以,page->active < cachep->num為真則表示slab中的空閒對象還未耗盡,這時候可以繼續分配空閒對象
- slab_get_obj獲取空閒對象的地址,ac_put_obj把空閒對象填充到本地對象緩衝池,兩個函數的實現細節如下↓
- 136~140行:最後把slab從原有鏈表摘下,如果active==num表示該頁對應slab已沒有空閒對象所以將其加入slabs_full(無空閒對象slabs)鏈表,否則將其加入slabs_partial(部分空閒對象slabs)鏈表
cache_alloc_refill實現3
- 第142行:如前面所說如果在對象部分空閒和全部空閒slab鏈表都為空(沒有slab)那就會跳轉到此處,第一次運行keme_cache_alloc的時候程序會運行到此處,因為這時候三大slab鏈表還是空的。cache_grow函數的實現如下↓
cache_grow函數
cache_grow實現1
- colour_next表示下一個slab應該包含的colour數。colour從0開始增加,每個slab的增加1。而一旦colour數目達到cachep->colour,那colour會從0重新計算
- 有__GFP_WAIT使能中斷
- 調用kmem_getpages根據之前創建的slab分配器描述符初始化的參數申請物理內存頁,其實現細節如下↓
cache_grow實現2
- 申請到內存頁後調用alloc_slabmgmt函數用於設計slab空間佈局,實現細節如下↓
- slab的地址和對應freelist地址初始化給page->slab_cache和page->freelist
- 調用cache_init_objs函數初始化slab對象,其實現細節如下↓
- 把slab對應的內存頁從原始LRU位置轉移到slab對象全部空閒鏈表
- 統計slab下空閒對象的總數
kmem_getpages函數
kmem_getpages實現
- 判斷是否有可回收SLAB
- 調用alloc_pages_exact_node申請內存頁面,假設為gfporder=0,則表示申請1頁
- 根據cachep->flags設定slab對應的zone狀態是否未可回收屬性的內存,最終返回申請到的內存頁
slab_get_obj函數
slab_get_obj
- get_free_obj獲取slab中空閒對象的索引
- slab_get_obj根據空閒對象的索引找到其地址並返回,返回一個空閒對象,active就自加1表示多了一個slab活躍對象
alloc_slabmgmt函數
alloc_slabmgmt實現
- 分為兩種可能,一種freelist在slab之外,一種freelist包含在slab之內
- 假設申請到gfporder=0為例, freelist在slab之上的情況:
0*cacheline+freelist+obj+obj...=第1個slab;
1*cacheline+freelist+obj+obj...=第2個slab;
2*cacheline+freelist+obj+obj...=第3個slab;
依次類推。 - page->s_mem指向freelist的首地址
cache_init_objs函數
cache_init_objs實現
- index_to_obj根據索引得到slab中每個空閒對象的地址,調用ctor構建對象
- 初始化freelist數組值,從0到cachep->num,表示slab中所有對象的索引值
以上就是slab對象分配的核心,總的來說slab分配步驟就以下幾步:
1.檢查本地對象緩衝池,如果本地緩衝池沒有就看看共享緩衝池有沒有,有的話直接從共享緩衝池拿
2.本地和共享緩衝池都沒有就先去slab“對象部分空閒”鏈表看有沒有,如果沒有就到slab“對象完全空閒隊列”看有沒有,如果有就把它轉移到本地對象緩衝池,並將其所在LRU的位置更新到最新的位置
3.如果上述2條鏈表都是空的,那就要先申請內存頁,申請頁面數量以slab描述符創建的時候初始化的gfporder為準,頁面申請成功就是初始化slab內存佈局,按colour...+colour+freelist[nr_objs]+obj...+obj的空間佈局進行初始化,再加入相應的空閒對象隊列。其中page->active表示所損耗的空閒對象數目,freelist是一個數組,數組的大小對應slab中obj的數量,freelist的元素存放的是對應每個obj的索引
閱讀更多 Linux家族的糟心事 的文章