1 頁式管理 1.1 分段機制存在的問題 分段,是指將程式所需要的記憶體空間大小的虛擬空間,通過映射機制映射到某個物理地址空間(映射的操作由硬體完成)。分段映射機制解決了之前操作系統存在的兩個問題: 1. 地址空間沒有隔離 2. 程式運行的地址不確定 不過分段方法存在一個嚴重的問題:記憶體的使用效率低。 ...
1 頁式管理
1.1 分段機制存在的問題
分段,是指將程式所需要的記憶體空間大小的虛擬空間,通過映射機制映射到某個物理地址空間(映射的操作由硬體完成)。分段映射機制解決了之前操作系統存在的兩個問題:
- 地址空間沒有隔離
- 程式運行的地址不確定
不過分段方法存在一個嚴重的問題:記憶體的使用效率低。
分段的記憶體映射單位是整個程式;如果記憶體不足,被換入換出到磁碟的空間都是整個程式的所需空間,這會造成大量的磁碟訪問操作,並且嚴重降低了運行速度.
事實上,很多時候程式運行所需要的數據只是很小的一部分,加入到記憶體的數據大小可能會很小,並沒有必要整體的寫入和寫出.
分頁機制解決了上面分段方法所存在的一個記憶體使用效率問題;其核心思想是系統為程式執行文件中的第x頁分配了記憶體中的第y頁,同時y頁會添加到進程虛擬空間地址的映射表中(頁表),這樣程式就可以通過映射訪問到記憶體頁y了。
1.2 分頁存儲的基本內容
分頁的基本方法是將地址空間人為地等分成某一個固定大小的頁;每一頁大小由硬體來決定,或者是由操作系統來決定(如果硬體支持多種大小的頁)。目前,以大小為4KB的分頁是絕大多數PC操作系統的選擇.
- 邏輯空間等分為頁;並從0開始編號
- 記憶體空間等分為塊,與頁面大小相同;從0開始編號
- 分配記憶體時,以塊為單位將進程中的若幹個頁分別裝入
關於進程分頁. 當我們把進程的虛擬地址空間按頁來分割,常用的數據和代碼會被裝在到記憶體;暫時沒用到的是數據和代碼則保存在磁碟中,需要用到的時候,再從磁碟中載入到記憶體中即可.
這裡需要瞭解三個概念:
- 虛擬頁(VP, Virtual Page),虛擬空間中的頁;
- 物理頁(PP, Physical Page),物理記憶體中的頁;
- 磁碟頁(DP, Disk Page),磁碟中的頁。
虛擬記憶體的實現需要硬體的支持,從Virtual Address到Physical Address的映射,通過一個叫MMU(Memory Mangement Unit)的部件來完成
2 分頁機制支持
2.1 硬體分頁支持
分頁單元(paging unit)把線性地址轉換成物理地址。其中的一個關鍵任務就是把所請求的訪問類型與線性地址的訪問許可權相比較,如果這次記憶體訪問是無效的,就產生一個缺頁異常。
頁 : 為了更高效和更經濟的管理記憶體,線性地址被分為以固定長度為單位的組,成為頁。頁內部連續的線性地址空間被映射到連續的物理地址中。這樣,內核可以指定一個頁的物理地址和對應的存取許可權,而不用指定全部線性地址的存取許可權。這裡說頁,同時指一組線性地址以及這組地址包含的數據
- 頁框:分頁單元把所有的 RAM 分成固定長度的頁框(page frame)(有時叫做物理頁)。每一個頁框包含一個頁(page),也就是說一個頁框的長度與一個頁的長度一致。頁框是主存的一部分,因此也是一個存儲區域。區分一頁和一個頁框是很重要的,前者只是一個數據塊,可以存放在任何頁框或磁碟中。
頁表:把線性地址映射到物理地址的數據結構稱為頁表(page table)。頁表存放在主存中,併在啟用分頁單元之前必須由內核對頁表進行適當的初始化。
2.2 常規的32bit分頁
常規4KB分頁,32位的線性地址被分成3個域
Directory(目錄) | Table(頁表) | Offset(偏移量) |
---|---|---|
最高10位 | 中間10位 | 最低12位 |
線性地址的轉換分為兩步完成,每一步都基於一種轉換表,第一種轉換表稱為頁目錄表(page directory),第二種轉換表稱為頁表(page table)。
為什麼需要兩級呢?
目的在於減少每個進程頁表所需的 RAM 的數量。如果使用簡單的一級頁表,將需要高達2^20 個表項來表示每個進程的頁表,即時一個進程並不使用所有的地址,二級模式通過職位進程實際使用的那些虛擬記憶體區請求頁表來減少記憶體容量。
每個活動的進程必須有一個頁目錄,但是卻沒有必要馬上為所有進程的所有頁表都分配 RAM,只有在實際需要一個頁表時候才給該頁表分配 RAM。
頁目錄項和頁表項有同樣的結構,每項都包含下麵的欄位:
欄位 | 描述 |
---|---|
Present標誌 | 如果被置為1,所指的頁(或頁表)就在主存中;如果該標誌為0,則這一頁不在主存中,此時這個表項剩餘的位可由操作系統用於自己的目的。如果執行一個地址轉換所需的頁表項或頁目錄項中Present標誌被清0,那麼分頁單元就把該線性地址轉換所需的頁表項或頁目錄項中Present標誌被清0,那麼分頁單元就把該線性地址存放在控制寄存器cr2中,並產生14號異常:缺頁異常。 |
--- | 包含頁框物理地址最高20位的欄位。由於每一個頁框有4KB的容量,它的物理地址必須是4096的倍數,因此物理地址的最低12位總為0.如果這個欄位指向一個頁目錄,相應的頁框就含有一個頁表;如果它指向一個頁表,相應的頁框就含有一頁數據 |
Accessed標誌 | 每當分頁單元對相應頁框進行定址時就設置這個標誌。當選中的頁被交換出去時,這一標誌就可以由操作系統使用。分頁單元從來不重置這個標誌,而是必須由操作系統去做 |
Dirty標誌 | 只應用於頁表項中。每當對一個頁框進行寫操作時就設置這個標誌。與Accessed標誌一樣,當選中的頁被交換出去時,這一標誌就可以由操作系統使用。分頁單元從來不重置這個標誌,而是必須由操作系統去做。 |
Read/Write標誌 | 含有頁或頁表的存取許可權(Read/Write或Read)。 |
User/Supervisor標誌 | 含有訪問頁或頁表所需的特權級。 |
PCD和PWT標誌 | 控制硬體高速緩存處理頁或頁表的方式。 |
Page Size標誌 | 只應用於頁目錄項。如果設置為1,則頁目錄項指的是2MB或4MB頁框。 |
Global標誌 | 只應用於頁表項。這個標誌是在Pentium Pro中引入的,用來防止常用頁從TLB高速緩存中刷新出去。只有在cr4寄存器的頁全局啟用(Page Global Enable, PGE)標誌置位時這個標誌才起作用。 |
正在使用的頁目錄的物理地址存放在控制寄存器CR3中。
瞭解了以上結構之後,我們看看如何從線性地址轉換到物理地址的 :
- 線性地址中的 Directory 欄位決定頁目錄中的目錄項,目錄項指向適當的頁表
- 線性地址中的 Table 欄位又決定頁表的頁表項,頁表項含有頁所在頁框的物理地址
- 線性地址中的 Offset 地段決定了頁框內的相對位置,由於 offset 為 12 為,所以一頁含有 4096 位元組的數據
Directory欄位和Table欄位都是10位長,因此頁目錄和頁表都可以多達1024項。那麼一個頁目錄可以定址到高達
1024*1024*4096=232
個存儲單元,這和32位地址所期望的一樣。
2.3 物理地址擴展(PAE)分頁機制和擴展分頁(PSE)
處理器所支持的RAM容易受到連接到地址匯流排上的地址管腳樹限制. 早期Intel處理器從80386到Pentium使用32位物理地址.
從理論上講, 這樣的系統可以使用高達2^32=4GB的RAM, 而實際上, 由於用戶進程現行地址空間的需要, 4GB的虛擬地址按照1:3的比例劃分給內核虛擬地址空間和進程虛擬地址空間. 則內核只能直接對1GB的線性地址空間進行定址.
然而, 大型伺服器需要大於4GB的RAM來同時運行數以錢計的進程, 所以必須擴展32位80x86架構所支持的RAM容量.
Intel通過在它的處理器上把管腳數從32增加到36滿足這樣的需要, 從Pentinum Pro開始, Intel所有處理器的定址能力可達到2^36=64GB, 但是只有引入一種新的分頁機制才能把32位現行地址轉換為36位物理地址才能使用所增加的物理地址.
從Pentinum Pro處理器開始, Intel引入一種叫做物理地址擴展(Physical Address Extension, PAE)的機制.
從Pentium模型開始,80x86微處理器引入了擴展分頁(externded paging),也叫頁大小擴展[Page Size Extension], 它允許頁框大小為4MB而不是4KB。擴展分頁用於把大段連續的線性地址轉換成相應的物理地址,在這種情況下,內核可以不用中間頁表進行地址轉換,從而節省記憶體並保留TLB項。
但是Linux並沒有採用這種機制
正如前面所述,通過設置頁目錄項的Page Size標誌啟用擴展分頁功能。在這種情況下,分頁單元把32位線性地址分成兩個欄位:
- Directory:最高10位。
- Offfset:其餘22位。
擴展分頁和正常分頁的頁目錄項基本相同,除了
- Page Size標誌必須被設置。
- 20位物理地址欄位只有最高10位是有意義的。這是因為每一個物理地址都是在以4MB為邊界的地方開始的,故這個地址的最低22位為0。
通過設置cr4處理器寄存器的PSE標誌能使擴展分頁與常規分頁共存
Intel為了支持PAE改變了分頁機制
- 64GB的RAM被分成了2^24個頁框, 頁表項的物理地址欄位從20位擴展到了24位. 因為PAE頁表項必須包含12個標誌位和24個物理地址位, 總數之和為36, 頁表項大小從32位擴展到了64位, 結果, 一個4KB的頁表項包含512個表項而不是1024個表項
- 引入一個頁目錄指針表(Page Directory Pointer Table, PDPT)的頁表新級別, 它由4個64位表項組成.
- cr3控制寄存器包含一個27位的頁目錄指針表(PDPT)基地址欄位. 因為PDPT存放在RAM的前4GB中, 併在32位元組(2^5)的倍數上對其, 因此27位足以表示這種表的基地址
- 當把線性地址映射到4KB的頁時(頁目錄項中的PS標準清0), 32位線性地址將按照如下方式解釋
欄位 | 描述 | 位數 |
---|---|---|
cr3 | 指向一個PDPT | crs寄存器存儲 |
PGD | 指向PDPT中4個項中的一個 | 位31~30 |
PMD | 指向頁目錄中512項中的一個 | 位29~21 |
PTE | 指向頁表中512項中的一個 | 位20~12 |
page offset | 4KB頁中的偏移 | 位11~0 |
- 當把現行地址映射到2MB的頁時(頁目錄項中的PS標誌置為1), 32位線性地址按照如下方式解釋
欄位 | 描述 | 位數 |
---|---|---|
cr3 | 指向一個PDPT | crs寄存器存儲 |
PGD | 指向PDPT中4個項中的一個 | 位31~30 |
PMD | 指向頁目錄中512項中的一個 | 位29~21 |
page offset | 2MB頁中的偏移 | 位20~0 |
總之, 一旦cr3被設置, 就可能定址高達4GB RAM, 如果我們期望堆更多的RAM進行定址, 就必須在cr3中放置一個新值, 或改變PDPT的內容.
但是PAE的主要問題是線性地址仍然是32位長, 這就需要內核黑客用同一線性地址映射不同的RAM區. 很顯然, PAE並沒有擴大進程的線性地址空間, 因為它只處理物理地址. 此外, 只有內核能夠修改進程的頁表, 所以在用戶態下運行的程式不可能使用大於4GB的物理地址空間. 另一方面, PAE允許內核使用容量高達64GB的RAM, 從而顯著的增加系統中的進程數目
2.4 64位系統中的分頁
32位處理器普遍採用兩級分頁。然而兩級分頁並不適用於採用64位系統的電腦。
原因如下 :
首先假設一個大小為4KB的標準頁,4KB覆蓋2^12個地址,所以offset欄位是12位。如果我們現在決定僅僅使用64位中的48位來定址(這個限制仍然能是我們自在地擁有256TB的定址空間!),剩下的48-12=36位被分配給Table和Directory欄位。如果我們決定為兩個欄位個預留18位,那麼每個進程的頁目錄和頁表都含有2^18個項,即超過256000個項。
由於這個原因,所有64位處理器的硬體分頁系統都使用了額外的分頁級別。使用的級別數量取決於處理器的類型。
平臺名稱 | 頁大小 | 定址使用位數 | 分頁級別 | 線性地址分級 |
---|---|---|---|---|
alpha | 8KB | 43 | 3 | 10+10+10+13 |
ia64 | 4KB | 39 | 3 | 9+9+9+12 |
ppc64 | 4KB | 41 | 3 | 10+10+9+12 |
x86_64 | 4KB | 48 | 4 | 9+9+9+9+12 |
註:ia64是intel的一門高端技術,不與x86_64系統相容
IA-32e Paging機制下線性地址映射到4KB的頁
2.5 硬體保護方案
與頁和頁表相關的特權級只有兩個,因為特權由前面“常規分頁”一節中所提到的User/Supervisor標誌所控制。若這個標誌為0,只有當CPL小於3(這意味著對於Linux而言,處理器處於內核態)時才能對頁定址;若該標誌為1,則總能對頁定址。
此外,與段的3種存取許可權(讀,寫,執行)不同的是,頁的存取許可權只有兩種(讀,寫)。如果頁目錄項或頁表項的Read/Write標誌等於0,說明相應的頁表或頁是只讀的,否則是可讀寫的。
3 總結
假設每個進程都占用了4G的線性地址空間,頁表共含1M個表項,每個表項占4個位元組,那麼每個進程的頁表要占據4M的記憶體空間。為了節省頁表占用的空間,我們使用兩級頁表。每個進程都會被分配一個頁目錄,但是只有被實際使用頁表才會被分配到記憶體裡面。一級頁表需要一次分配所有頁表空間,兩級頁表則可以在需要的時候再分配頁表空間。
3.1 為什麼使用多級頁表
假設每個進程都占用了4G的線性地址空間,頁表共含1M個表項,每個表項占4個位元組,那麼每個進程的頁表要占據4M的記憶體空間。為了節省頁表占用的空間,我們使用兩級頁表。每個進程都會被分配一個頁目錄,但是只有被實際使用頁表才會被分配到記憶體裡面。一級頁表需要一次分配所有頁表空間,兩級頁表則可以在需要的時候再分配頁表空間。
3.2 x86_32兩級頁表結構
兩級表結構的第一級稱為頁目錄,存儲在一個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.
3.3 線性地址到物理地址的轉換
CR3包含著頁目錄的起始地址,用32位線性地址的最高10位A31~A22作為頁目錄的頁目錄項的索引,將它乘以4,與CR3中的頁目錄的起始地址相加,形成相應頁表的地址。
從指定的地址中取出32位頁目錄項,它的低12位為0,這32位是頁表的起始地址。用32位線性地址中的A21~A12位作為頁表中的頁面的索引,將它乘以4,與頁表的起始地址相加,形成32位頁面地址。
將A11~A0作為相對於頁面地址的偏移量,與32位頁面地址相加,形成32位物理地址。
3.4 擴展分頁
從奔騰處理器開始,Intel微處理器引進了擴展分頁,它允許頁的大小為4MB
在擴展分頁的情況下,分頁機制把32位線性地址分成兩個域:最高10位的目錄域和其餘22位的偏移量。
3.5 頁面高速緩存
由於在分頁情況下,每次存儲器訪問都要存取兩級頁表,這就大大降低了訪問速度。所以,為了提高速度,在386中設置一個最近存取頁面的高速緩存硬體機制,它 自動保持32項處理器最近使用的頁面地址,因此,可以覆蓋128K位元組的存儲器地址。當進行存儲器訪問時,先檢查要訪問的頁面是否在高速緩存中,如果在, 就不必經過兩級訪問了,如果不在,再進行兩級訪問。平均來說,頁面高速緩存大約有98%的命中率,也就是說每次訪問存儲器時,只有2%的情況必須訪問兩級分頁機構。這就大大加快了速度.