設計原理
SLAB 分配器多年以來一直位於 Linux 核心的記憶體管理部分的核心地帶,核心黑客們一般不願意主動去更改它的代碼,因為它實在是非常複雜,而且在大多數情況下,它的工作完成的相當不錯。但是,隨著大規模多處理器系統和 NUMA系統的廣泛套用,SLAB 分配器逐漸暴露出自身的嚴重不足:
較多複雜的佇列管理。在 SLAB 分配器中存在眾多的佇列,例如針對處理器的本地對象快取佇列,slab 中空閒對象佇列,每個 slab 處於一個特定狀態的佇列中,甚至緩衝區控制結構也處於一個佇列之中。有效地管理這些不同的佇列是一件費力且複雜的工作。
slab 管理數據和佇列的存儲開銷比較大。每個 slab 需要一個 struct slab 數據結構和一個管理所有空閒對象的 kmem_bufctl_t(4 位元組的無符號整數)的數組。當對象體積較少時,kmem_bufctl_t 數組將造成較大的開銷(比如對象大小為32位元組時,將浪費 1/8 的空間)。為了使得對象在硬體高速快取中對齊和使用著色策略,還必須浪費額外的記憶體。同時,緩衝區針對節點和處理器的佇列也會浪費不少記憶體。測試表明在一個 1000 節點/處理器的大規模 NUMA 系統中,數 GB 記憶體被用來維護佇列和對象的引用。
緩衝區記憶體回收比較複雜。
對 NUMA 的支持非常複雜。SLAB 對 NUMA 的支持基於物理頁框分配器,無法細粒度地使用對象,因此不能保證處理器級快取的對象來自同一節點。
冗餘的 Partial 佇列。SLAB 分配器針對每個節點都有一個 Partial 佇列,隨著時間流逝,將有大量的 Partial slab 產生,不利於記憶體的合理使用。
性能調優比較困難。針對每個 slab 可以調整的參數比較複雜,而且分配處理器本地快取時,不得不使用自旋鎖。
調試功能比較難於使用。
1.較多複雜的佇列管理。在 SLAB 分配器中存在眾多的佇列,例如針對處理器的本地對象快取佇列,slab 中空閒對象佇列,每個 slab 處於一個特定狀態的佇列中,甚至緩衝區控制結構也處於一個佇列之中。有效地管理這些不同的佇列是一件費力且複雜的工作。
2.slab 管理數據和佇列的存儲開銷比較大。每個 slab 需要一個 struct slab 數據結構和一個管理所有空閒對象的 kmem_bufctl_t(4 位元組的無符號整數)的數組。當對象體積較少時,kmem_bufctl_t 數組將造成較大的開銷(比如對象大小為32位元組時,將浪費 1/8 的空間)。為了使得對象在硬體高速快取中對齊和使用著色策略,還必須浪費額外的記憶體。同時,緩衝區針對節點和處理器的佇列也會浪費不少記憶體。測試表明在一個 1000 節點/處理器的大規模 NUMA 系統中,數 GB 記憶體被用來維護佇列和對象的引用。
3.緩衝區記憶體回收比較複雜。
4.對 NUMA 的支持非常複雜。SLAB 對 NUMA 的支持基於物理頁框分配器,無法細粒度地使用對象,因此不能保證處理器級快取的對象來自同一節點。
5.冗餘的 Partial 佇列。SLAB 分配器針對每個節點都有一個 Partial 佇列,隨著時間流逝,將有大量的 Partial slab 產生,不利於記憶體的合理使用。
6.性能調優比較困難。針對每個 slab 可以調整的參數比較複雜,而且分配處理器本地快取時,不得不使用自旋鎖。
7.調試功能比較難於使用。
為了解決以上 SLAB 分配器的不足之處,核心開發人員 Christoph Lameter 在 Linux 核心 2.6.22 版本中引入一種新的解決方案:SLUB 分配器。SLUB 分配器特點是簡化設計理念,同時保留 SLAB 分配器的基本思想:每個緩衝區由多個小的 slab 組成,每個 slab 包含固定數目的對象。SLUB 分配器簡化了kmem_cache,slab 等相關的管理數據結構,摒棄了SLAB 分配器中眾多的佇列概念,並針對多處理器、NUMA 系統進行最佳化,從而提高了性能和可擴展性並降低了記憶體的浪費。為了保證核心其它模組能夠無縫遷移到 SLUB 分配器,SLUB 還保留了原有 SLAB 分配器所有的接口 API 函式。