Linux 的記憶體管理

Linux 作業系統原本是在 IA32 (x86) 處理器上設計的,由於 IA32 具有 MMU 單元,因此大部分的 Linux 都預設支援虛擬記憶體機制。然而,在許多的嵌入式處理器中,並沒有 MMU 單元,於是有 Jeff Dionne 等人於 1998 年開始將 Linux 中的 MMU 機制去除並改寫,後來釋出了不具有 MMU 的 µClinux 版本。

曾經有一段時間,嵌入式的系統開發者必須決定應使用具有 MMU 的 Linux 或不具 MMU 的 µClinux,但是後來在2.5.46版的 Linux 中,決定將 µClinux 納入核心中,所以後來的 Linux 核心已經包含了 µClinux 的功能,可以選擇是否要支援 MMU 單元。

由於 Tovarlds 最早是在 IA32 (x86) 中發展出 Linux 作業系統的,因此 Linux 的記憶體管理機制深受 x86 處理器的影響。要瞭解 Linux 的記憶體管理機制,首先必須先理解 x86 的MMU 記憶體管理單元。

IA32 (x86) 的記憶體管理單元

Intel 的 IA32 (Pentium處理器) 採用了 GDT 與 LDT 兩種表格,其中的 LDT 分段表 (Local Descriptor Table) 是給一般行程使用的,而 GDT 分段表 (Global Descriptor Table) 則包含各行程共享的分段,通常由作業系統使用。

IA32 同時具有分段與分頁單元,可以支援『純粹分段』、『單層分頁式分段』與『雙層分頁式分段』等三種組合。其『邏輯位址』 (Logical Address) 經過分段單位轉換後,稱為『線性位址』 (Linear Address),再經過分頁單位轉換後,稱為真實位址 (Physical Address)。其簡要的轉換過程如圖 1 所示。

圖 1. IA32的兩階段位址轉換過程

IA32 邏輯位址 (虛擬位址) 的長度是 48 位元,分為選擇器 S (selector : 16 bits) 與偏移量 D (offset : 32bits) 兩部分。其中的選擇器欄位中的pr兩個位元用來記錄保護屬性,g位元記錄表格代碼 (可指定目標表格為 GDT 或 LDT),另外13個位元則記錄分段碼 (s)。

IA32的分段表LDT與GDT各自包含4096個項目,每個項目都含有 『分段起始位址』 (base)、『分段長度』 (limit) 與數個『輔助位元』 (aux) 等三種欄位。其中 base 與 limit 的功能與一般分段表相同,而輔助位元則用來記錄分段屬性。這兩個表格都可以用來將邏輯位址轉換成線性位址,是 IA32 中的分段單元。詳細的轉換過程如圖 2 所示。

圖 2. IA32分段模式

除了選擇器 (S) 的分段轉換過程之外,位移 (D) 部分會經過分頁表轉換成真實位址。位移 (D) 可再細分為三段 (p1, p2, d),其中的 p1 式分頁目錄代號,p2式分頁表代號,而 d 則是分頁內位移。IA32 可以不採用分頁機制,或採用單層分頁機制,甚至可以採用雙層分頁機制。其中的分頁目錄 PD 是第一層分頁,分頁表 PT 則是第二層分頁。當採用單層分頁時,每頁的大小為 4-MB。而採用雙層分頁時,每頁的大小為 4-KB。圖 3 顯示了IA32 的分頁機制之轉換過程。

圖 3. IA32的分頁模式

IA32 可以不使用分頁機制,直接將分段後的線性位址輸出,此時相當於一般的分段模式,其輸出位址如圖 4 中的 L 所示。如果使用單層分頁機制,則相當於一般的分段式分頁,其輸出位址如圖 4 中的 M1 所示。如果採用兩層分頁機制,則形成雙層分頁式分段體系,其輸出位址如圖 4 中的 M2所示。圖 4 顯示了IA32 MMU單元完整的轉換過程。

圖 4. IA32的分頁式分段模式

從 IA32 的 MMU 單元設計中,我們可以看到 IA32 處理器留下了相當大的選擇空間給作業系統,作業系統可以自行選擇使用哪一種分段分頁機制,這與作業系統的設計有密切的關係。

Linux 的記憶體管理機制

X86 版本的 Linux 利用 GDT 指向核心的分頁,然後用 LDT 指向使用者行程的分頁。LDT 中所記載的是各個行程的分段表,以及行程的狀態段 (Task State Segment : TSS)。而 GDT 中則會記載這些分段表的起始點,TSS 起始點,以及核心的各分段起點。圖 5 顯示了 x86 版的 Linux 的分段記憶體管理機制。