【x86】內存頁式管理
基本概念
將內存切分為等大小的頁面,例如一個頁位4096 B = \(2^{12}\) B,也就是4 KB。這樣,內存的最小單位變成了4 KB。
4 GB (\(2^{32}\) B) 的內存地址范圍為:0x 0000 0000 ~ 0x FFFF FFFF
4 KB 的內存范圍為:0x 0000 0000 ~ 0x 0000 0FFF
也就是說,如果要變成以4 KB為一頁來管理內存,地址就變成了
0x FFFF F000 ~ 0x FFFF FFFFF
? ? ? ? ? .
? ? ? ? ? .
? ? ? ? ? .
? ? ? ? ? .
0x 0000 2000 ~ 0x 0000 2FFF
0x 0000 1000 ~ 0x 0000 1FFF
0x 0000 0000 ~ 0x 0000 0FFF
這樣的排布,也就是說,4 GB被分為 \(2^{32-12} = 2^{20}\) 個大小為4 KB的塊。
這樣,地址的高20位,對應著16進制地址的高5位,就代表了分頁地址。找一個地方把這高20位保存起來,再加上12位的頁屬性,就變成了一個4個字節大小的,頁表項或者叫做分頁入口。
| 頁表 Page Table | 分頁 |
|---|---|
| 頁地址高20位+頁屬性 | 0x FFFF F000 ~ 0x FFFF FFFFF |
| ... | ... |
| ... | ... |
| ... | ... |
| ... | ... |
| 頁地址高20位+頁屬性 | 0x 0000 2000 ~ 0x 0000 2FFF |
| 頁地址高20位+頁屬性 | 0x 0000 1000 ~ 0x 0000 1FFF |
| 頁地址高20位+頁屬性 | 0x 0000 0000 ~ 0x 0000 0FFF |
左邊的頁表,總共\(2^{20}\)個頁表項,一個頁表項是4 個字節,也就是 \(2^{2+20} = 2^{22}\) = 4 MB的大小,即頁表大小是4 MB就可以管理整個4 GB大小的所有內存空間。
這個4 MB的頁表也同樣用4 KB的分頁來管理,\(\frac{4MB}{4KB}=\frac{2^{22}}{2^{12}}=2^{10}=1024\) ,所以總共4MB的頁表也被劃分為了1024個分頁。
一個分頁中,有\(\frac{4KB}{4}=\frac{2^{12}}{2^2}=1024\) 個頁表項。
這1024個頁表,放在內存中,不是專門有一個區域的,也就是可能完全雜亂隨機,不按照順序存放的。因此,我們需要一個頁表目錄,去存放記錄頁表項的入口都在哪里。
仍然是使用4個字節來存放頁表入口(4個字節:頁表地址高20位+頁表屬性),因為我們是32位的系統,總共1024個頁表項,那就是\(2^{12}\)=4KB大小的頁表目錄,剛好是一個分頁的大小。其實這都是嚴格設計的,保證了數據結構的4K對齊。
使用專門的寄存器CR3,來保存頁目錄的起始地址。
之前有說過,為了簡化內存的管理,會使用平坦內存模型。也就是每個段都是4GB,每個段的起始地址都是0x 0000 00000。因此,每個段的區別是讀寫訪問權限的不同。
分頁地址計算
加上了今天的內存分頁之后,物理地址的計算就變成:
邏輯地址(段選擇子+偏移地址) --分段機制找到段基地址--> 線性地址(虛擬地址)(段基地址+偏移地址) --分頁機制--> 物理地址
每個32位的線性地址被劃分為三個部分:頁目錄索引(10位),頁表索引(10位):偏移(12位即4K)。
首先根據分段機制,計算出來了線性地址。依據下面的步驟進行地址轉換:
1、 裝入進程的頁目錄地址(操作系統在調度進程時,把這個地址裝入CR3寄存器),CR3中的值是預先存儲好的。
2、 根據現行線性地址的前10位,在頁目錄中找對應的索引項,頁目錄中的項是一個頁表的地址。
3、 根據線性地址的中間10位,在頁表中找到頁的起始地址。
4、 將頁的起始地址與線性地址的最后12位相加,得到物理地址。
這樣的二級模式是可以覆蓋4G的物理地址空間的。
64位時代
CPU進入了64位時代之后,分段機制逐漸被弱化。但是,為了保證兼容性,沒有被拋棄,段機制也無法完全關閉,而是通過將所有的段基址全部都當作0,而且忽略段選擇子中的段界限的大小。

段寄存器中的DS/ES/SS被直接忽略,而FS/GS/CS只關注其中的幾位,也就是一部分屬性的大小。

這樣,就進入了平坦模式。只需要關注Paging的幾種方法


浙公網安備 33010602011771號