核心內部與作業系統建構

slab 配置器(板層配置器)

/ slab /

核心會一遍又一遍地建立與銷毀數量龐大、又小又一模一樣的物件——這裡一個新的檔案描述符、那裡一個網路緩衝區標頭、每個新行程一個任務結構。每次都向頁配置器要記憶體會很浪費:為一個 200 位元組的物件動用一整個 4 KB 的頁,浪費掉大半個頁,而且不斷地初始化全新物件也很慢。slab 配置器以保有一池池預先成形、可重複使用的同型物件來解決這個問題。想像一家忙碌的小餐館,不會在每位客人之間都把盤子洗淨、擦乾、再放回櫥櫃;它在櫃檯邊就放著各式各樣的乾淨盤子一疊,要用立刻給一個,用過的就丟回那一疊。

具體來說,slab 配置器(以及它在 Linux 中的後繼者 slub)建立在夥伴系統之上。它向夥伴配置器要來整頁一次,然後把每一頁切成許多大小都符合某種特定物件型別的格子——一個 slab 就是這樣一頁(或一連串頁),裡頭滿是物件大小的格子,而一個 cache 則是專屬於某一型別的所有 slab 的集合。當核心需要,比方說,一個新的 inode 時,配置器以 O(1) 的時間從 inode cache 取出一個空閒格子,而且往往已經部分初始化好了;當該 inode 被釋放時,格子回到該 cache 的空閒清單,而不是退回夥伴系統。這把許多小物件緊密地塞進頁裡(浪費極少)、避免反覆配置頁的開銷,並把同型別的物件擺在一起,對 CPU 快取很友善。

它之所以重要,是因為它回答了「核心為什麼不能直接用使用者空間的 malloc」這個問題:malloc 仰賴系統呼叫與一個核心裡並不存在的使用者堆積、會以中斷情境中所禁止的方式睡眠或發生錯誤,而且並未針對核心那如洪流般的微小固定大小物件做調校。一個常見的混淆是以為 slab 取代了夥伴系統;其實它位於其上——夥伴系統發出頁,slab 配置器把那些頁零售成小物件。

Linux 保有一些具名的 cache,你可以用 slabtop 工具檢視,例如一個給 dentry(目錄項)物件、一個叫 inode_cache。當你開啟許多檔案時,核心以常數時間從這些 cache 取出 dentry 與 inode 物件;關閉檔案則把格子歸還 cache,準備好應付下一次開啟——不必為每一個都跑回頁配置器一趟。

預先切好的同型物件池,讓配置變成快速取出一個,而非一次全新的頁請求。

slab 並不會神奇地消除浪費:每個 cache 都為它的物件型別保留整頁,因此被某個 cache 佔住的記憶體在 slab 被回收之前無法自由地給其他人用。而且因為同型別的物件聚在一起,核心裡的釋放後使用(use-after-free)漏洞可被精心整理某個 slab cache 而加以利用。

又稱
slabslub allocatorobject cache物件快取