分頁機制在段機制之後進行,以完成線性—物理地址的轉換過程。段機制把邏輯地址轉換為線性地址,分頁機制進一步把該線性地址再轉換為物理地址。
硬體中的分頁
分頁機制由CR0中的PG位啟用。如PG=1,啟用分頁機制,並使用本節要描述的機制,把線性地址轉換為物理地址。如PG=0,禁用分頁機制,直接把段機制產生的線性地址當作物理地址使用。分頁機制管理的對象是固定大小的儲存塊,稱之為頁 (page)。分頁機制把整個線性地址空間及整個物理地址空間都看成由頁組成,線上性地址空間中的任何一頁,可以映射為物理地址空間中的任何一頁(我們把物理空間中的一頁叫做一個頁面或頁框(page frame))。
80386 使用4K位元組大小的頁。每一頁都有4K位元組長,並在4K位元組的邊界上對齊,即每一頁的起始地址都能被4K整除。因此,80386把4G位元組的線性地址空間,劃分為1G個頁面,每頁有4K位元組大小。分頁機制通過把線性地址空間中的頁,重新置放到物理地址空間來進行管理,因為每個頁面的整個4K位元組作為一個單位進行映射,並且每個頁面都對齊4K位元組的邊界,因此,線性地址的低12位經過分頁機制直接地作為物理地址的低12位使用。
為什麼使用兩級頁表
假設每個進程都佔用了4G的線性地址空間,頁表共含1M個表項,每個表項佔4個位元組,那麼每個進程的頁表要佔據4M的記憶體空間。為了節省頁表佔用的空間,我們使用兩級頁表。每個進程都會被分配一個頁目錄,但是只有被實際使用頁表才會被分配到記憶體裡面。一級頁表需要一次分配所有頁資料表空間,兩級頁表則可以在需要的時候再分配頁資料表空間。
兩級頁表結構
兩級表結構的第一級稱為頁目錄,儲存在一個4K位元組的頁面中。頁目錄表共有1K個表項,每個表項為4個位元組,並指向第二級表。線性地址的最高10位(即位31~位32)用來產生第一級的索引,由索引得到的表項中,指定並選擇了1K個二級表中的一個表。
兩級表結構的第二級稱為頁表,也剛好儲存在一個4K位元組的頁面中,包含1K個位元組的表項,每個表項包含一個頁的物理基地址。第二級頁表由線性地址的中間10 位(即位21~位12)進行索引,以獲得包含頁的物理地址的頁表項,這個物理地址的高20位與線性地址的低12位形成了最後的物理地址,也就是頁轉化過程輸出的物理地址。
頁目錄項
第31~12位是20位頁表地址,由於頁表地址的低12位總為0,所以用高20位指出32位頁表地址就可以了。因此,一個頁目錄最多包含1024個頁表地址。
第0位是存在位,如果P=1,表示頁表地址指向的該頁在記憶體中,如果P=0,表示不在記憶體中。
第1位是讀/寫位,第2位是使用者/管理員位,這兩位為頁目錄項提供硬體保護。當特權級為3的進程要想訪問頁面時,需要通過頁保護檢查,而特權級為0的進程就可以繞過頁保護。
第3位是PWT(Page Write-Through)位,表示是否採用寫透方式,寫透方式就是既寫記憶體(RAM)也寫快取,該位為1表示採用寫透方式
第4位是PCD(Page Cache Disable)位,表示是否啟用快取,該位為1表示啟用快取。
第5位是訪問位,當對頁目錄項進行訪問時,A位=1。
第7位是Page Size標誌,只適用於頁目錄項。如果置為1,頁目錄項指的是4MB的頁面,請看後面的擴充分頁。
第9~11位由作業系統專用,Linux也沒有做特殊之用。
頁面項
80386的每個頁目錄項指向一個頁表,頁表最多含有1024個頁面項,每項4個位元組,包含頁面的起始地址和有關該頁面的資訊。頁面的起始地址也是4K的整數倍,所以頁面的低12位也留作它用。
第31~12位是20位物理頁面地址,除第6位外第0~5位及9~11位的用途和頁目錄項一樣,第6位是頁面項專屬的,當對涉及的頁面進行寫操作時,D位被置1。
4GB的記憶體只有一個頁目錄,它最多有1024個頁目錄項,每個頁目錄項又含有1024個頁面項,因此,記憶體一共可以分成1024×1024=1M個頁面。由於每個頁面為4K個位元組,所以,儲存空間的大小正好最多為4GB。
線性地址到物理地址的轉換
32位線性地址到物理地址的轉換
1.CR3包含著頁目錄的起始地址,用32位線性地址的最高10位A31~A22作為頁目錄的頁目錄項的索引,將它乘以4,與CR3中的頁目錄的起始地址相加,形成相應頁表的地址。
2.從指定的地址中取出32位頁目錄項,它的低12位為0,這32位是頁表的起始地址。用32位線性地址中的A21~A12位作為頁表中的頁面的索引,將它乘以4,與頁表的起始地址相加,形成32位頁面地址。
3.將A11~A0作為相對於頁面地址的位移量,與32位頁面地址相加,形成32位物理地址。
擴充分頁
從奔騰處理器開始,Intel微處理器引進了擴充分頁,它允許頁的大小為4MB。
在擴充分頁的情況下,分頁機制把32位線性地址分成兩個域:最高10位的目錄域和其餘22位的位移量。
頁面快取
由於在分頁情況下,每次儲存空間訪問都要存取兩級頁表,這就大大降低了訪問速度。所以,為了提高速度,在386中設定一個最近存取頁面的快取硬體機制,它 自動保持32項處理器最近使用的頁面地址,因此,可以覆蓋128K位元組的儲存空間地址。當進行儲存空間訪問時,先檢查要訪問的頁面是否在快取中,如果在, 就不必經過兩級訪問了,如果不在,再進行兩級訪問。平均來說,頁面快取大約有98%的命中率,也就是說每次訪問儲存空間時,只有2%的情況必須訪問兩級分頁機構。這就大大加快了速度。
Linux中的分頁機制
Linux使用了一個適合32位和64位系統的分頁機制。
頁全域目錄
頁頂級目錄
頁中間目錄
頁表
頁全域目錄包含若干頁上級目錄的地址,頁上級目錄又依次包含若干頁中間目錄的地址,而頁中間目錄又包含若干頁表的地址。每一個頁表項指向一個頁框。線性地址因此被分成五個部分。圖中沒有顯示位元,因為每一部分的大小與具體的電腦體繫結構有關。
對於沒有啟用實體位址延伸的32位系統,兩級頁表已經足夠了。從本質上說,Linux通過使“頁上級目錄”位和“頁中間目錄”位全為0,徹底取消了頁上級目錄和頁中間目錄欄位。不過,頁上級目錄和頁中間目錄在指標序列中的位置被保留,以便同樣的代碼在32位系統和64位系統下都能使用。核心為頁上級目錄和頁中間目錄保留了一個位置,這是通過把它們的頁目錄項數設定為1,並把這兩個目錄項映射到頁全域目錄的一個合適的目錄項而實現的。
啟用了實體位址延伸的32 位系統使用了三級頁表。Linux的頁全域目錄對應80×86 的頁目錄指標表(PDPT),取消了頁上級目錄,頁中間目錄對應80×86的頁目錄,Linux的頁表對應80×86的頁表。
最後,64位系統使用三級還是四級分頁取決於硬體對線性地址的位的劃分。
總結
這裡我們不討論代碼實現,只關注原理。從上面的討論可以看到分頁機制主要依賴硬體的實現。Linux採用的四級頁表只是為了最大化相容不同的硬體實現,單就IA32架構的CPU來說,就有多種分頁實現,常規分頁機制,PAE機制等。
我們雖然討論的是Linux的分頁機制,實際上我們用了大部分篇幅來討論Intel CPU的分頁機制實現。因為Linux的分頁機制是建立在硬體基礎之上的,不同的平台需要有不同的實現。Linux在軟體層面構造的虛擬位址,最終還是要通過MMU轉換為物理地址,也就是說,不管Linux的分頁機制是怎樣實現的,CPU只按照它的分頁實現來解讀線性地址,所以Linux傳給CPU的線性地址必然是滿足硬體實現的。例如說:Linux在32位CPU上,它的四級頁表結構就會相容到硬體的兩級頁表結構。可見,Linux在軟體層面上做了一層 抽象,用四級頁表的方式相容32位和64位CPU記憶體定址的不同硬體實現。