1. 程式人生 > >SLAB與SLUB 記憶體管理器

SLAB與SLUB 記憶體管理器

SLUB記憶體分配器
譯者按:不知道讀者朋友們有沒有誤入過 /sys/slab 目錄,進過這個嚇人的目錄之後,你可能就很想知道它到底是怎麼回事,這和 slab 記憶體分配器有關,當然,更和 SLUB 記憶體分配器相關了,/sys/slab 和 slub 一同在 2.6.22 核心被引入,用於提升 cache 記憶體分配在多處理器系統中的效率。嗯,接下來還是看看這篇不是很久遠的考古譯文。

***
補充一下,目前SLUB也不是一帆風順的,和 /sys/slab 有關的東東也有很多問題,http://lwn.net/Articles/263329/ 這篇文章和之後的評論指出了這些問題,讓我們拭目以待吧,好了,現在可以繼續看這篇考古譯文了。
***

slab記憶體分配器多年以來一直位於核心的記憶體管理部分的核心地帶,它(在底層頁分配器之上)管理著特定大小的物件的快取,允許快速而且省空間的記憶體分配

。核心黑客們一般不願意去動slab的帶脈,因為它實在是非常複雜,而且在大多數情況下,它的工作完成的相當不錯。

Christoph Lameter就是那些感到 slab 工作的不是那麼好的少數人之一。長時間以來,他提出了一個長長的關於 slab 的問題列表。slab 分配器維護了大量的物件佇列,這些佇列可以讓記憶體分配更快,卻也增加了複雜度。此外,這些儲存開銷會隨著系統規模增大而增大:

每個節點、每個CPU都有 slab 物件佇列。alien cache 佇列甚至有一個為每個節點的每個cpu都儲存一個佇列的佇列陣列。對於大規模的系統,佇列數量和物件數量甚至會指數增長。在我們的有一千個節點/處理器的系統中,大約有幾G位元組的記憶體被用於存放那些佇列,這還不包括哪些在這些佇列上的物件。恐怕有一天所有的記憶體會都消耗在這些佇列上頭。

而且,每個 slab (一組一個或多個連續的用於分配物件的頁面) 都要在起始處包含大量的元資料 ,這使得物件的對其進一步變得困難了。而用於在記憶體緊張的時候情理 cache 的程式碼又進一步增加了複雜度……

Christoph 對此的處理是提出了一個SLUB allocator,用於替代 slab 程式碼
。通過取消了大量的佇列和相關開銷、簡化 slab 的結構,SLUB 承諾提供更好的效能和更好的系統可伸縮性,並且可以同時保持現有的 slab 分配器介面。

在 SLUB 分配器中,一個 slab 就是一組一個或多個頁面,封裝了固定大小的 物件。slab 內部沒有元資料,只是空閒物件組織在簡單的連結串列之中。當有一個分配請求的時候,第一個空閒物件就此被定位、從列表中刪除並返還給呼叫者。

在缺少每個slab的元資料的情況下,你可能會非常好奇第一個空閒的物件是如何被發現的。答案就在於 SLUB 分配器將這些資訊儲存在了系統記憶體映像之中 — 與這個 slab 所在的頁面相關的頁結構。讓 struct page 變大同樣是讓人不能認同的,所以SLUB 分配器通過增加了另一個 union 來讓這個複雜的結構更復雜了一些,從而解決了這個問題。最終結果是 struct page 增加了三個只在作為 slab 組成部分時才有效的欄位:

void *freelist;
short unsigned int inuse;
short unsigned int offset;

在用於 slab 時,freelist 指向 slab 中的第一個空閒物件,inuse 是 slab 已經分配出去的物件數量,而通過 offset ,分配器可以知道那裡可以找到指向連結串列中下一個空閒物件的指標。SLUB 分配器可以使用 RCU 來釋放物件,但是,如果想這麼做,它必須能夠將“下一個物件”的指標放在物件之外;offset 指標正是 SLUB 用來跟蹤指標放置位置的手段。

當 slab 被分配器建立的時候,沒有物件從中被分配過。一旦一個物件被分配了,它也就成為了一個儲存在 kmem_cache 結構中的一個連結串列中的“部分的” slab。作為一個以可伸縮性為目標的補丁,系統中的每個 NUMA 節點會有一個“部分的”連結串列。分配器盡力保證分配節點本地的記憶體,但是會在部分 slab 填滿系統之前到達其他節點的。

另外,還有一個每CPU的啟用 slab 的陣列,用於防止包括 NUMA 節點內部的各種 cache line bouncing。同時還有一個特別的執行緒(通過一個工作佇列)執行,監視每 CPU slab 的使用情況,如果一個CPU的 slab 沒有被用上,它還會被放回到部分列表中,以便被其他程序使用。

如果一個 slab 中的所有物件都被分配出去了,分配器就可以完全不用考慮這個 slab 了。一旦一個滿 slab 中的一個物件被釋放了,分配器就可以可以通過系統記憶體映像來重定位這個 slab,並將它放回到相應的部分連結串列之中去。如果一個給定的 slab 中的所有物件(通過 inuse 計數器標記)都被釋放了,那麼整個 slab 都將被放回到頁分配器中,以便重用。

SLUB 記憶體分配器的一個有趣的特性是它可以合併多個有相似物件尺寸和引數的 slab。其結果是系統中可以有更少的 slab 快取(據說可以減少 50%),更好的本地化 slab 分配和更少的 slab 記憶體碎片。該補丁宣告:

這個合併可以暴露出核心中迄今為止尚不為人所知的 bug,因為壞掉的物件現在可能難於放置,並會影響到臨近的物件。請開啟 sanity check 來發現這些問題


讓 bug 暴露出來總的來說是件好事,不過,過多使用 SLUB 分配器在那些新的 bug 沒有被找出來之前會引起一些奇異行為。

過於廣泛的使用是完全可能的:SLUB 分配器現在已經在 -mm 樹之中了,並且可能會被加入到 2.6.22 主線核心當中。簡化的程式碼非常誘人,據說有 5-10% 的效能提升。如果被加入到主線核心當中,SLUB 可以和當前的 slab 分配器(以及面向小系統的 SLOB)共存相當常一段時間。從長遠來看,當前的 slab 程式碼可能就要到了生命的終點了。