Linux 的記憶體管理

Linux 核心

簡介

中斷機制

行程管理

記憶體管理

檔案系統

輸出入系統

目的檔

動態連結

相關資訊

相關網站

相關文獻

最新修改

訊息

相關網站

參考文獻

最新修改

簡體版

English

簡介

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 所示。

IA32MMU.jpg

圖 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 所示。

IA32segment.jpg

圖 2. IA32分段模式

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

IA32page.jpg

圖 3. IA32的分頁模式

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

IA32pagesegment.jpg

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

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

Linux 的記憶體管理機制

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

LinuxMMU.jpg

圖 5. Linux 的記憶體管理機制

透過圖 5 的分段機制,Linux 可為每一個行程的段落分配一塊大小不等的區段。其中每一個區段又可能佔據許多個分頁,x86 版本的Linux 採用 IA32 的延伸分頁模式,利用 IA32『分段+雙層分頁』的延伸記憶體管理模式,因此每個頁框的大小為4KB。

當需要進行分段配置 (例如載入行程) 時,Linux會使用對偶式記憶體管理演算法 (Buddy System Algorithm) 配置分頁,在 Buddy 系統中,一個頁框代表一段連續的分頁,該演算法將的頁框區分為十種區塊大小,分別包含了 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 個連續的頁框,其中每個區塊的第一的頁框位置一定是區塊大小的倍數。舉例而言,一個包含 32 個頁框的區塊之起始位址一定是 32 * 4KB 的倍數。

Buddy 頁框分配系統

Buddy 系統的運作方法,乃是利用一個名為 free_area[10] 的陣列,該陣列中儲存了對應大小的位元映像圖 (bitmap),以記錄區塊的配置狀況。當有分頁配置需求時,Linux 會尋找大小足夠的最小區塊,舉例而言,如果需要13 個頁框,則 Linux 會從大小為 16 的頁框區中取出一個可用頁框,分配給需求者。但是如果大小為 16 的頁框區沒有可用頁框,則會從大小為 32 的頁框區取得,然後分成兩半,一半分配給需求者,另一半則放入大小為 16 的可用頁框區中。

當某頁框被釋放時,Buddy 系統會試圖檢查其兄弟頁框是否也處於可用狀態,若是則將兩個頁框合併以形成一個更大的可用頁框,放入可用頁框串列中。

Slab 記憶體配置器

當 Linux 需要配置的是小量的記憶體 (像是 malloc 所需的記憶體) 時,採用的是一種稱為 Slab Allocator 的配置器,其中被配置的資料稱為物件 (Object)。 Slab中的物件會被儲存在 Buddy 系統所分配的頁框中,假如要分配一個大小為 30 bytes 的物件時,Slab 會先向 Buddy 系統要求取得一個最小的分頁 (大小為4KB),然後分配個 Slab 配置器。然後 Slab 配置器會保留一些位元以記錄配置資訊,然後將剩下的空間均分為大小30 的物件。於是當未來再有類似的配置請求時,就可以直接將這些空的物件配置出去。

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License