分頁配置器(夥伴系統)
核心以固定大小的區塊管理實體記憶體,這些區塊稱為分頁(page,常是各 4 KiB)。必須有東西在驅動、分頁快取與核心其餘部分需要時,把這些分頁發出去並回收回來——而且它得做得快、又不能把記憶體切得支離破碎。分頁配置器就是那個東西;在 Linux 裡它用一個稱為夥伴系統的經典方案,是更高層核心記憶體工具賴以建立的基礎配置器。
夥伴系統的把戲是以大小為二的冪次分頁的區塊來管理記憶體:1 頁、2 頁、4 頁、8 頁等等。它維護自由串列,每種大小一個。當你請求譬如 4 頁時,它找一個空閒的 4 頁區塊;若沒有,它取一個空閒的 8 頁區塊,把它切成兩個 4 頁的半塊,稱為夥伴(buddies),給你其中一個,並把另一個記錄為空閒。當記憶體稍後被釋放時,配置器檢查那個區塊的夥伴(切割時的搭檔)是否也空閒——若是,兩個夥伴便被合併回它們所源自的較大區塊,而那次合併可向上連鎖。因為每個區塊的夥伴只需把一個位址位元做簡單的算術翻轉就能找到,所以切割與合併都非常快。
為何這個設計歷久不衰:對空閒夥伴的持續合併,積極地對抗外部碎片——空閒記憶體被打散成太小、無法滿足大請求的碎塊——使核心能在漫長的開機時間裡持續找到連續的多頁區塊。誠實的取捨是內部碎片:因為它只配置二的冪次大小,一個 5 頁的請求必須向上湊成一個 8 頁區塊,浪費 3 頁。這正是為何核心在分頁配置器之上層疊 slab/SLUB 配置器:夥伴系統高效地提供整頁,slab 配置器再把那些頁切成許多同樣大小的小物件,使一個幾百位元組的請求不至浪費掉一整頁。
請求 4 頁,只有 8 頁區塊空閒 -> 切成兩個 4 頁夥伴,回傳其一。稍後釋放它 -> 若其夥伴也空閒,合併回 8 頁(也許再到 16)。夥伴位址 = 區塊位址 XOR(區塊大小)。
切割二的冪次區塊以滿足請求;把釋放的夥伴合併回去以對抗碎片。夥伴是靠翻轉一個位址位元找到的。
夥伴系統抑制外部碎片,卻受內部碎片之苦:只發放二的冪次頁數,所以一個 5 頁的請求耗用一個 8 頁區塊。對小物件的這種浪費,正是 slab/SLUB 配置器疊在其上的原因——夥伴提供整頁,slab 再細分它們。