在分頁機制下,Linear Address Space(線性地址空間)和 Physical Address Space(物理地址空間)都會被切割成以頁(Page)為單位的區塊,通常來說,每個頁的大小是 4 KB。Paging 機制的作用就是在建立線性地址空間和物理地址空間之間的頁映射關係。
一個記憶體地址在Paging 機制下,可以看成頁索引跟位移量的組合,當 MMU 接收到一個地址查詢請求時,會先將Linear Address Space 的頁索引換成 Physical Address Space的頁索引,在加回 Offset 就能夠拿到對應的物理地址。
在 CPU 內部,維護 Linear Address Space Page 和 Physical Address Space Page 對應關係的表格稱為 Page Table(頁表)。最簡單的實現方式是使用線性地址的一部份作為頁表索引 (非正式: Linear Page Index),然後每個頁表項目(Page Table Entry, PTE)保存對應的 Physical Page Index (非正式)。
然而,這樣的實現方式會產生很大的成本。例如,在 32 位元的地址架構下,最大地址空間為 4 GB,這意味著可以劃分出 4 * 1024 * 1024 / 4 = 1048576 個 4 KB 大小的頁。由於大多數程式並不會使用全部 4 GB 的地址空間,因此大多數 PTE 都不會連結到物理頁,造成極大的浪費。
為了解決這個問題,實務上會採用多層級的 Page Table。在 Intel 的架構下,最多會使用到五層頁表,不過這邊我們以四層為例。
當 MMU 在查找 Linear Address 與 Physical Address 的對應關係時,會依序通過每一層頁表來找到最終的 PTE。這些頁表層級從上到下分別是:Page Map Level 4 (PML4) Table、Page Directory Pointer Table、Page Directory 以及 Page Table。每一層頁表都使用 Linear Address 裡面的一段作為 entry index(索引),每個 entry 保存下一層頁表的起始地址。
這種多層結構的好處在於,如果一段連續的地址空間沒有對應的物理記憶體,作業系統無需保存該區域的下層頁表,只需在上層頁表標註為無效即可。
例如,若一個程式使用 10 MB 的空間,會使用到 10 * 1024 / 4 = 2560 個 PTE。由於每張頁表可以包含 512 個 entry,因此需要 5 張 Page Table 和 1 張 Page Directory Table (PDT) 來索引這些頁表。因此整個系統只需 1 張 PML4T、1 張 PDPT、1 張 PDT 以及 5 張 Page Table,共計 (1+1+1+5) * 512 = 4096 個條目,相較於單層頁表的 1048576 個條目,這節省了大量的空間。
每個 Process 都有各自獨立的虛擬地址空間,因此也會有各自的 Page Table。當 CPU 執行不同的 Process 時,會通過 page-directory base register (PDBR) 來記錄當前 Process 的 PML4 Table 地址,在 Intel 架構中稱為 CR3 暫存器。
儘管多層頁表能夠有效管理記憶體空間,但它也帶來了存取效率的問題。每次存取記憶體時,MMU 要存取4次記憶體,經過多層索引才能找到目標物理地址,這會造成顯著的延遲。為了解決這個問題,MMU 引入了 TLB(Translation Lookaside Buffer),它是一張暫存表,記錄部分 Linear Address Page 和 Physical Address Page 的對應關係。當 MMU 收到查詢請求時,會先查詢 TLB,若查無結果,才會進行多層頁表的索引
在 Linux Kernel 中,page
結構負責維護每一個物理頁的使用情況。
// linux/mm_types.h
struct page {
...
atomic_t _refcount;
...
}
Linux 在初始化時,為實體記憶體中的每個 Page 建立一個對應的 page
結構,並記錄其使用狀況。當程式試圖存取沒有映射到物理頁的線性地址空間時,MMU 會觸發一個 Page Fault Exception,進入 Kernel Mode 分配新的物理記憶體。Kernel 會找到一個閒置的 page(_refcount
為 0),並更新頁表來建立映射。
透過分頁機制,CPU 將線性地址空間與物理地址空間完全切割開來,這帶來了幾個重要的特性:
Paging 機制是記憶體管理的核心技術。程式能夠有效且安全地使用記憶體,同時操作系統也能夠保證不同程式之間的記憶體隔離與管理。