Slab Allocator
問題背景
Kernel 需要反覆配置和釋放固定大小的資料結構(例如 task_struct、inode、file)。
如果每次都向 Buddy System 要一塊頁框再切割:
- 速度慢(每次都要走 buddy 的分裂流程)
- 會造成嚴重的 external fragmentation
Jeff Bonwick 在 1994 年提出 slab allocator,核心直覺是:
既然 kernel 編譯後物件種類就固定了,不如替每種物件預留一批「槽位」,用完放回來,不要每次都重新向底層申請。
三層架構
把 slab allocator 想成一間「預製零件工廠」:
Cache(某種零件的倉庫)
└── Slab(倉庫裡的一塊貨架,由連續頁框組成)
└── Object(貨架上切好的一個槽位)
| 層級 | 說明 |
|---|---|
| Object | 某個 kernel 資料結構的一個實例,大小 = sizeof(struct xxx),固定不變 |
| Slab | 一塊或多塊實體連續的頁框(physically contiguous pages),被切成一格一格,每格容納一個 object |
| Cache | 管理某種 kernel 資料結構的 metadata,由一個或多個 slab 組成,例如 inode_cache、dentry_cache |
Cache ≠ 預先填滿的 objects
Cache 建立時(
kmem_cache_create())只是初始化 metadata,尚無任何 slab 或 object。 Slab 是「按需建立」的:第一次kmem_cache_alloc()時,cache 發現沒有 free slot,才向 Buddy System 申請頁框並建立第一個 slab。
運作流程
flowchart LR A[需要 object] --> B{cache 有 free slot?} B -- 有 --> C[標記為 used,回傳指標] B -- 沒有 --> D{有空的 slab?} D -- 有 --> E[從空 slab 配置] D -- 沒有 --> F[向 Buddy System 申請新頁框] F --> G[建新 slab] --> E --> C
- 配置:需要某個物件 → 從 cache 取一個 free object → 標記為 used → 回傳指標
- 釋放:用完 → object 標記回 free → 留在 cache 裡,不還給 buddy system
- 擴張:所有 slab 都滿 → 向 Buddy System 申請新頁框,建新的 slab
優點
1. 減少 Internal Fragmentation
Buddy System 給的是 bytes 的 page,object size 不一定整除。但 slab 把同一種大小的 object 打包在一起,浪費被壓縮到最小。
2. 快速配置
需要 object 時只要把一個 free slot 標記為 used 再回傳指標,省去:
- 向 buddy system 申請頁框
- 複雜的切割計算
- 重新初始化(某些實作保留物件的部分初始化狀態,稱為 object reuse)
變體
| 變體 | 適用場景 | 特點 |
|---|---|---|
| SLAB | 原始設計 | 完整功能,metadata 較複雜 |
| SLUB | 現代 Linux 預設 | 簡化 per-slab metadata,多核心效能更好 |
| SLOB | 嵌入式 / 記憶體極度有限 | 用簡單 linked list,佔用最少記憶體 |