天天看點

【原創】(十一)Linux記憶體管理slub配置設定器

<code>Read the fucking source code!</code> --By 魯迅

<code>A picture is worth a thousand words.</code> --By 高爾基

說明:

Kernel版本:4.14

ARM64處理器,Contex-A53,雙核

使用工具:Source Insight 3.5, Visio

之前的文章分析的都是基于頁面的記憶體配置設定,而小塊記憶體的配置設定和管理是通過塊配置設定器來實作的。目前核心中,有三種方式來實作小塊記憶體配置設定:<code>slab, slub, slob</code>,最先有<code>slab</code>配置設定器,<code>slub/slob</code>配置設定器是改進版,<code>slob</code>配置設定器适用于小記憶體嵌入式裝置,而<code>slub</code>配置設定器目前已逐漸成為主流塊配置設定器。接下來的文章,就是以<code>slub</code>配置設定器為目标,進一步深入。

先來一個初印象:

【原創】(十一)Linux記憶體管理slub配置設定器

有四個關鍵的資料結構:

<code>struct kmem_cache</code>:用于管理<code>SLAB緩存</code>,包括該緩存中對象的資訊描述,per-CPU/Node管理slab頁面等;

關鍵字段如下:

<code>struct kmem_cache_cpu</code>:用于管理每個CPU的<code>slab頁面</code>,可以使用無鎖通路,提高緩存對象配置設定速度;

<code>struct kmem_cache_node</code>:用于管理每個Node的<code>slab頁面</code>,由于每個Node的通路速度不一緻,<code>slab</code>頁面由Node來管理;

<code>struct page</code>:用于描述<code>slab頁面</code>,<code>struct page</code>結構體中很多字段都是通過<code>union</code>聯合體進行複用的。

<code>struct page</code>結構中,用于<code>slub</code>的成員如下:

圖來了:

【原創】(十一)Linux記憶體管理slub配置設定器

針對Slub的使用,可以從三個次元來分析:

slub緩存建立

slub對象配置設定

slub對象釋放

下邊将進一步分析。

在核心中通過<code>kmem_cache_create</code>接口來建立一個<code>slab緩存</code>。

先看一下這個接口的函數調用關系圖:

【原創】(十一)Linux記憶體管理slub配置設定器

<code>kmem_cache_create</code>完成的功能比較簡單,就是建立一個用于管理<code>slab緩存</code>的<code>kmem_cache</code>結構,并對該結構體進行初始化,最終添加到全局連結清單中。<code>kmem_cache</code>結構體初始化,包括了上文中分析到的<code>kmem_cache_cpu</code>和<code>kmem_cache_node</code>兩個字段結構。

在建立的過程中,當發現已有的<code>slab緩存</code>中,有存在對象大小相近,且具有相容标志的<code>slab緩存</code>,那就隻需要進行merge操作并傳回,而無需進一步建立新的<code>slab緩存</code>。

<code>calculate_sizes</code>函數會根據指定的<code>force_order</code>或根據對象大小去計算<code>kmem_cache</code>結構體中的<code>size/min/oo</code>等值,其中<code>kmem_cache_order_objects</code>結構體,是由頁面配置設定<code>order</code>值和<code>對象數量</code>兩者通過位域拼接起來的。

在建立<code>slab緩存</code>的時候,有一個先雞後蛋的問題:<code>kmem_cache</code>結構體來管理一個<code>slab緩存</code>,而建立<code>kmem_cache</code>結構體又是從<code>slab緩存</code>中配置設定出來的對象,那麼這個問題是怎麼解決的呢?可以看一下<code>kmem_cache_init</code>函數,核心中定義了兩個靜态的全局變量<code>kmem_cache</code>和<code>kmem_cache_node</code>,在<code>kmem_cache_init</code>函數中完成了這兩個結構體的初始化之後,相當于就是建立了兩個<code>slab緩存</code>,一個用于配置設定<code>kmem_cache</code>結構體對象的緩存池,一個用于配置設定<code>kmem_cache_node</code>結構體對象的緩存池。由于<code>kmem_cache_cpu</code>結構體是通過<code>__alloc_percpu</code>來配置設定的,是以不需要建立一個相關的<code>slab緩存</code>。

<code>kmem_cache_alloc</code>接口用于從slab緩存池中配置設定對象。

看一下大體的調用流程圖:

【原創】(十一)Linux記憶體管理slub配置設定器

從上圖中可以看出,配置設定slab對象與<code>Buddy System</code>中配置設定頁面類似,存在快速路徑和慢速路徑兩種,所謂的快速路徑就是<code>per-CPU緩存</code>,可以無鎖通路,因而效率更高。

整體的配置設定流程大體是這樣的:優先從<code>per-CPU緩存</code>中進行配置設定,如果<code>per-CPU緩存</code>中已經全部配置設定完畢,則從<code>Node</code>管理的slab頁面中遷移<code>slab頁</code>到<code>per-CPU緩存</code>中,再重新配置設定。當<code>Node</code>管理的slab頁面也不足的情況下,則從<code>Buddy System</code>中配置設定新的頁面,添加到<code>per-CPU緩存</code>中。

還是用圖來說明更清晰,分為以下幾步來配置設定:

<code>fastpath</code>

快速路徑下,以原子的方式檢索per-CPU緩存的freelist清單中的第一個對象,如果freelist為空并且沒有要檢索的對象,則跳入慢速路徑操作,最後再傳回到快速路徑中重試操作。

【原創】(十一)Linux記憶體管理slub配置設定器

<code>slowpath-1</code>

将per-CPU緩存中page指向的slab頁中的空閑對象遷移到freelist中,如果有空閑對象,則freeze該頁面,沒有空閑對象則跳轉到<code>slowpath-2</code>。

【原創】(十一)Linux記憶體管理slub配置設定器

<code>slowpath-2</code>

将per-CPU緩存中partial連結清單中的第一個slab頁遷移到page指針中,如果partial連結清單為空,則跳轉到<code>slowpath-3</code>。

【原創】(十一)Linux記憶體管理slub配置設定器

<code>slowpath-3</code>

将Node管理的partial連結清單中的slab頁遷移到per-CPU緩存中的page中,并重複第二個slab頁将其添加到per-CPU緩存中的partial連結清單中。如果遷移的slab中空閑對象超過了<code>kmem_cache.cpu_partial</code>的一半,則僅遷移<code>slab頁</code>,并且不再重複。

如果每個Node的partial連結清單都為空,跳轉到<code>slowpath-4</code>。

【原創】(十一)Linux記憶體管理slub配置設定器

<code>slowpath-4</code>

從<code>Buddy System</code>中擷取頁面,并将其添加到per-CPU的page中。

【原創】(十一)Linux記憶體管理slub配置設定器

<code>kmem_cache_free</code>的操作,可以看成是<code>kmem_cache_alloc</code>的逆過程,是以也分為快速路徑和慢速路徑兩種方式,同時,慢速路徑中又分為了好幾種情況,可以參考<code>kmem_cache_alloc</code>的過程。

調用流程圖如下:

【原創】(十一)Linux記憶體管理slub配置設定器

效果如下:

快速路徑釋放

快速路徑下,直接将對象傳回到freelist中即可。

【原創】(十一)Linux記憶體管理slub配置設定器

put_cpu_partial

<code>put_cpu_partial</code>函數主要是将一個剛freeze的slab頁,放入到partial連結清單中。

在<code>put_cpu_partial</code>函數中調用<code>unfreeze_partials</code>函數,這時候會将per-CPU管理的partial連結清單中的slab頁面添加到Node管理的partial連結清單的尾部。如果超出了Node的partial連結清單,溢出的slab頁面中沒有配置設定對象的slab頁面将會傳回到夥伴系統。

【原創】(十一)Linux記憶體管理slub配置設定器

add_partial

添加slab頁到Node的partial連結清單中。

【原創】(十一)Linux記憶體管理slub配置設定器

remove_partial

從Node的partial連結清單移除slab頁。

【原創】(十一)Linux記憶體管理slub配置設定器

具體釋放的流程走哪個分支,跟對象的使用情況,partial連結清單的個數<code>nr_partial/min_partial</code>等相關,細節就不再深入分析了。

作者:LoyenWang

出處:https://www.cnblogs.com/LoyenWang/

公衆号:<b>LoyenWang</b>

版權:本文版權歸作者和部落格園共有

轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接配接;否則必究法律責任

繼續閱讀