內存管理(20)分配slab對象

相關源碼:

mm/slab.c


slab分配器描述符創建好後,接下來要做的就是系統是怎樣分配這些slab對象的?slab對象的分配函數為kmem_cache_alloc,不過實際的分配在slab_alloc函數中處理看下它的實現:

kmem_cache_alloc->slab_alloc函數

內存管理(20)分配slab對象

slab_alloc實現1


內存管理(20)分配slab對象

slab_alloc實現2

  • 關閉本地中斷,調用__do_cache_alloc函數來分配slab對象,其實現細節如下↓

__do_cache_alloc函數

內存管理(20)分配slab對象

__do_cache_alloc實現

  • 第53行:調用____cache_alloc函數來分配slab對象
  • 第60行:如果slab對象分配失敗,則使用____cache_alloc_node從其他內存節點分配slab對象

____cache_alloc函數

內存管理(20)分配slab對象

____cache_alloc實現

  • 獲取cachep描述符的本地對象緩衝池
  • 本地對象緩衝池剛創建的時候avail為0,見 所以執行後面的步驟
  • 本地對象緩衝池沒有可用的對象,調用cache_alloc_refill函數向本地對象緩衝池填充對象。其實現細節如下↓

cache_alloc_refill函數

內存管理(20)分配slab對象

cache_alloc_refill實現1

  • 第97行:獲取本地內存節點ID
  • 第101行:獲取本地對象緩衝池地址
  • 第106行:獲取內存節點對應的slab節點
  • 第110~112行:共享對象緩衝池是否有空閒對象,如果有則將空閒對象填充到本地對象緩衝池


內存管理(20)分配slab對象

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)鏈表
內存管理(20)分配slab對象

cache_alloc_refill實現3

  • 第142行:如前面所說如果在對象部分空閒和全部空閒slab鏈表都為空(沒有slab)那就會跳轉到此處,第一次運行keme_cache_alloc的時候程序會運行到此處,因為這時候三大slab鏈表還是空的。cache_grow函數的實現如下↓

cache_grow函數

內存管理(20)分配slab對象

cache_grow實現1

  • colour_next表示下一個slab應該包含的colour數。colour從0開始增加,每個slab的增加1。而一旦colour數目達到cachep->colour,那colour會從0重新計算
  • 有__GFP_WAIT使能中斷
  • 調用kmem_getpages根據之前創建的slab分配器描述符初始化的參數申請物理內存頁,其實現細節如下↓


內存管理(20)分配slab對象

cache_grow實現2

  • 申請到內存頁後調用alloc_slabmgmt函數用於設計slab空間佈局,實現細節如下↓
  • slab的地址和對應freelist地址初始化給page->slab_cache和page->freelist
  • 調用cache_init_objs函數初始化slab對象,其實現細節如下↓
  • 把slab對應的內存頁從原始LRU位置轉移到slab對象全部空閒鏈表
  • 統計slab下空閒對象的總數

kmem_getpages函數

內存管理(20)分配slab對象

kmem_getpages實現

  • 判斷是否有可回收SLAB
  • 調用alloc_pages_exact_node申請內存頁面,假設為gfporder=0,則表示申請1頁
  • 根據cachep->flags設定slab對應的zone狀態是否未可回收屬性的內存,最終返回申請到的內存頁

slab_get_obj函數

內存管理(20)分配slab對象

slab_get_obj

  • get_free_obj獲取slab中空閒對象的索引
  • slab_get_obj根據空閒對象的索引找到其地址並返回,返回一個空閒對象,active就自加1表示多了一個slab活躍對象

alloc_slabmgmt函數

內存管理(20)分配slab對象

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函數

內存管理(20)分配slab對象

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的索引


分享到:


相關文章: