分頁機制

本文爲<x86彙編語言:從實模式到保護模式> 第16章筆記緩存

 

由於段的長度不定, 在分配內存時, 可能會發生內存中的空閒區域小於要加載的段, 或者空閒區域遠遠大於要加載的段. 在前一種狀況下, 須要另外尋找合適的空閒區域; 在後一種狀況下, 分配會成功, 但太過於浪費. 爲了解決這個問題, 從80386處理器開始, 引入了分頁機制. 分頁功能從整體上來講, 是用長度固定的頁來代替長度不必定的段, 藉此解決因段長度不一樣而帶來的內存空間管理問題. 儘管操做系統也能夠用軟件來實施固定長度的內存分配, 但太過於複雜, 由處理器固件來作這件事, 可使速度和效率最大化.操作系統

分頁機制概述

簡單的分頁模型

處理器中有負責分段管理的段部件, 每一個程序或任務都有本身的段, 這些段都用段描述符定義. 隨着程序的執行, 當要訪問內存, 就用段地址加上偏移量, 段部件就會輸出一個線性地址. 在單純的分段模式下, 線性地址就是物理地址..net

一旦決定採用頁式內存管理, 就應當把4GB內存分紅大小相同的頁. 頁的最小單位是4KB, 也就是4096字節, 用十六進制表示就是0x1000. 所以, 第一個頁的物理地址就是0x00000000, 第2個頁的物理地址是0x00001000, 第3個頁的物理地址是0x00002000....最後一個頁的物理地址是0xfffff000. 這樣,  4GB內存劃分爲1048576(0x100000)個頁. 很顯然, 頁的物理地址, 其低12位始終爲0.設計

段管理機制對於Intel處理器來講是最基本的, 任什麼時候候都沒法關閉. 也就是說, 即便啓用頁管理功能, 分段機制依然是起做用的, 段部件依然工做.code

如上圖所示, 內存的分配設計段空間的分配和頁分配. 左邊是虛幻的, 或者說虛擬的4GB內存空間, 稱爲虛擬內存; 右邊是實實在在的內存, 被分紅1048576個4KB的頁面(每一個方框4KB, 灰色表明已分配).blog

在分頁模式下, 操做系統能夠建立一個爲全部任務公用的4GB虛擬內存空間, 也能夠爲每個任務建立獨立的4GB虛擬內存空間, 這都是可行的. 當一個程序加載時, 操做系統既要在左邊的虛擬內存中分配段空間, 又要在右邊的物理內存中分配相應的頁面. 所以, 第一步驟是尋找空閒的段空間, 該段空間既沒有被其餘程序使用, 也沒有被同一程序內的其餘段使用. 好比上圖, 假設已經成功找到並分配了一個段空間, 基地址爲0x00200000, 長度爲8200字節.索引

頁的最小尺寸是4KB, 也就是4096字節, 所以, 8200字節的段, 須要佔用3個頁面, 其中最後一個頁面只用了8個字節, 其他都是浪費着, 但這可有可無, 若是容許頁共享, 多個段或多個程序能夠用同一個頁來存放各自的數據. 在分段以後, 操做系統的任務就是把段拆開, 並分別映射到物理頁. 注意, 段必須是連續的, 但不要求所分配的頁都是連續的, 挨在一塊兒的.ip

就上圖中的列子來講, 該段有8200字節, 須要分配3個頁面. 操做系統在物理內存中搜索可用的空閒頁, 接下來, 要創建線性地址和也之間的對應關係, 在圖中, 0x200000~0x00200FFF對應着物理地址爲0x00002000的頁, 0x00201000~0x00201FFF對應着0x00004000, 0x00202000~0x00202007對應着0x00007000的頁, 固然, 這裏只是示例, 線性地址區間和頁的對應關係能夠隨意.內存

4GB虛擬內存空間不可能用來保存任何數據, 由於它是虛擬的, 它只是用來指示內存的使用狀況. 當操做系統加載一個程序並建立爲任務時, 操做系統在虛擬內存空間尋找空閒的段, 並映射到空閒的頁, 而後, 到真正開始加載程序時, 再把本來屬於段的數據按頁的尺寸拆開, 分開寫入對應的頁中.get

從段部件輸出的是線性地址, 或者叫作虛擬地址. 爲了根據線性地址找到頁的物理地址, 操做系統必須維護一張表, 把線性地址轉換成物理地址, 這是一個反過程.

如上圖所示, 由於有1048576個頁, 故轉換表有1048576個表項. 這是個一維表格, 每一個表項佔4字節, 內容爲頁的物理地址. 這個表格的用法是這樣的: 由於頁的尺寸是4KB, 故, 線性地址的低12位可用於訪問頁內偏移, 高20位可用於指定一個物理頁. 所以, 把線性地址的高20位當成索引, 乘以4, 做爲表內偏移量, 從表中取出一個雙字, 那就是該線性地址作對應的頁的物理地址. 舉個例子: mov edx, [0x0002]    執行這條指令, 段部件用段地址0x00200000加上指令中給出的偏移量0x2002,  獲得線性地址0x00200002. 線性地址的高20位是表格索引, 即0x00200, 將索引乘以4, 獲得0x00800, 這就是表內偏移, 看圖, 從該單元能夠取出一個雙字0x00007000, 這就是頁物理地址. 線性地址的低12位是頁內偏移, 用頁物理地址加上頁內偏移量, 就是最終的物理內存地址. 0x00007000加上0x0002, 獲得0x00007002, 這就是實際要訪問的物理內存地址. 這裏有個問題, 爲何表內表內偏移爲0x000800的地方, 會剛好是物理地址0x00007000,  而不是其餘頁地址呢? 當程序加載時, 操做系統會首先在虛擬內存中分配段, 而後, 根據段須要分紅多少頁, 來搜索空閒頁面. 當段較大時, 要按頁的尺寸分紅好幾個地址區段, 操做系統用每一個區段的首地址, 取高20位, 乘以4, 做爲偏移量訪問表格, 並將分配給區段的頁的物理地址寫入該表項. 最後, 把本來須要寫入每一個區段的程序數據, 寫到對應的頁中. 注意了, 在頁式內存管理中, 頁面的管理和分配是獨立的, 和分段以及段地址沒有關係.操做系統所要作的, 就是尋找空閒頁面, 把它分配給須要的段, 並將頁的物理地址填寫到映射表內.  很顯然, 也很重要的結論是, 線性地址, 包括線性地址空間, 和頁面分配機制沒有關係.

基於以上特色, 同時爲了充分挖掘分頁內存管理的潛力, 通常來講, 每一個任務均可以擁有4GB的虛擬內存空間; 同時, 每一個任務都有本身的4GB虛擬內存空間, 可是, 很重要的是, 在整個系統中, 物理頁面是統一調配的. 考慮這樣一種情景: 任務A有一個段, 基地址爲0x00050000, 長度爲3000本身, 系統爲它分配了物理地址0x08001000的頁. 過了一會, 任務B加載了, 它也有一個段, 基地址也是0x00050000, 長度爲4096字節, 此時, 操做系統爲它分配了另一個不一樣的, 物理地址爲0x00700000的頁.  在這種狀況下, 在任務A內訪問線性地址0x00050006, 訪問的實際上是物理地址0x08001006; 在任務B內訪問一樣的線性地址時, 訪問的實際上是物理地址0x00700006.

另外一個問題是, 每一個任務都有4GB虛擬內存空間, 而物理內存只有一個, 最大也才4GB, 根本不夠分的. 事實上, 確實不夠分, 可是操做系統能夠暫時將不用頁退避到磁盤, 調入立刻要使用的頁, 經過這種手段來實現分頁內存管理.

以上, 就是基本的段頁式內存管理機制. 基本的段頁式內存管理示意圖:

頁目錄, 頁表和頁

咱們知道, 爲了完成從虛擬地址(線性地址)到物理地址的轉換, 操做系統應當爲每一個任務準備一張頁映射表. 由於任務的虛擬地址空間爲4GB, 能夠分出1048576個頁, 因此, 映射表須要1048756個表項, 又由於每一個表項4字節, 故映射表總大小爲4MB. 沒錯, 這張表很大, 要佔用至關一部分空間, 考慮到在實踐中, 沒有哪一個任務會真的用到全部表項, 充其量只是很小一部分, 這就很浪費了.  爲了解決這個問題, 處理器設計了層次化的分頁結構.

分頁結構層次化的主要手段是不採用單一的映射表, 取而代之的是頁目錄表和頁表. 以下圖所示:

首先, 由於4GB的虛擬內存空間對應着1048576個4KB頁, 能夠隨機的抽取這些頁, 將它們組織在1024個頁表內, 每一個頁表能夠容納1024個頁. 頁表內的每一個項目叫作頁表項, 佔4字節, 存放的是頁的物理地址, 故每一個頁表的大小是4KB, 正好是一個標準頁的長度. 注意, 頁在頁表內的分佈是隨機的, 哪一個頁位於哪一個頁表中, 這是沒有規律的.

如圖所示, 在將1048576個頁歸攏到1024個頁表以後, 接着, 再用一個表來指向1024個頁表, 這就是頁目錄表(Page Directory Table: PDT), 和頁表同樣, 頁目錄項的長度爲4字節, 填寫的是頁表的物理地址, 共指向1024個頁表, 因此頁目錄表的大小是4KB, 正好一個標準頁的長度.

這樣的層次化分頁結構是每一個任務都擁有的, 或者說, 每一個任務都有本身的頁目錄和頁表. 以下圖所示, 在處理器中有個控制寄存器CR3, 存放着當前任務頁目錄的物理地址, 故又叫作頁目錄基址寄存器(Page Directory Base Register: PDBR). 每一個任務都有本身的TSS, 其中就包括了CR3寄存器域, 存放了任務本身的頁目錄物理地址. 當任務切換時, 處理器切換到新任務開始執行, 而CR3寄存器的內容也被更新, 以指向新任務的頁目錄位置. 相應的, 頁目錄又指向一個個的頁表, 這就使得每一個任務都只在本身的地址空間內運行.  從下圖能夠看出, 頁目錄和頁表也是普通的頁, 混跡於所有的物理頁中. 它們和普通頁的不一樣支持僅僅在於功能不同. 當任務撤銷以後, 它們和任務所佔用的普通頁同樣會被回收, 並分配給其餘任務.

地址變換的具體過程

對於Intel處理器來講, 有關分頁, 最簡單和最基本的機制就是這些; CR3寄存器給出了頁目錄的物理地址; 頁目錄給出了全部頁表的物理地址, 而每一個頁表給出了它所包含的頁的物理地址. 好了, 該清楚的都清楚了, 惟一還不明白的, 應該是如何用這種層次性的分頁結構把線性地址轉換成物理地址? 這裏舉個例子, 某任務加載後, 在4GB虛擬地址空間建立了一個段, 起始地址爲0x00800000, 段界限爲0x5000, 字節粒度. 當前任務執行時, 段寄存器DS指向該段. 又假設執行了下面一條指令

  1. mov edx, [0x1050]  
mov edx, [0x1050]

此時, 段部件會輸出線性地址0x00801050. 在沒有開啓分頁機制時, 這就是要訪問的物理地址. 但如今開啓了分頁機制, 因此這是一個下虛擬地址, 要通過頁部件轉換, 才能獲得物理地址.

 

以下圖所示, 處理器的頁部件專門負責線性地址到物理地址的轉換工做. 它首先將段部件送來的32位線性地址分爲3段, 分別是高10位, 中間10位, 低12位. 高10位是頁目錄的索引, 中間10位是頁表的索引, 低12位則做爲頁內偏移量來用.

當前任務頁目錄的物理地址在處理器的CR3寄存器中, 假設它的內容爲0x00005000. 段管理部件輸出的線性地址是0x00801050, 其二進制的形式如圖中給出. 高10位是十六進制的0x002, 它是頁目錄表內的索引, 處理器將它乘以4(由於每一個目錄項4字節), 做爲偏移量訪問頁目錄. 最終處理器從物理地址00005008處取得頁表的物理地址0x08001000.

線性地址的中間10位爲0x001, 處理器用它做爲頁表索引取得頁的物理地址. 將該值乘以4, 做爲偏移量訪問頁表. 最終, 處理器又從物理地址08001004處取得頁的物理地址, 這就是咱們一直努力尋找的那個頁.

頁的物理地址是0x0000c000, 而線性地址的低12位是數據所在的業內偏移量. 故處理器將它們相加, 獲得物理地址0x0000C050, 這就是線性地址0x00801050所對應的物理地址, 要訪問的數據就在這裏.

注意, 這種變換不是平白無故的, 而是事先安排好的. 當任務加載時, 操做系統先建立虛擬的段, 並根據段地址的高20位決定它要用到哪些頁目錄項和頁表項. 而後, 尋找空閒的也, 將本來應該寫入段中的數據寫到一個或者多個頁中, 並將頁的物理地址填寫到相對應的頁表項中. 只有這樣作了, 當程序運行的時候, 才能以相反的順序進行地址變換, 並找到正確的數據.

頁目錄項, 頁表項, CR3和打開分頁

頁目錄項和頁表項

頁目錄和頁表中分別存放爲頁目錄項和頁表項, 它們的格式以下:

能夠看出, 在頁目錄和頁表中, 只保存了頁表或者頁物理地址的高20位. 緣由很簡單, 頁表或者頁的物理地址, 都要求必須是4KB對齊的, 以便於放在一個頁內, 故其低12位全是0. 在這種狀況下, 能夠只關心其高20位, 低12位安排其餘用途.

  • P 是存在位, 爲1時, 表示頁表或者頁位於內存中. 不然, 表示頁表或者頁不在內存中, 必須先予以建立, 或者從磁盤調入內存後方可以使用.
  • RW 是讀/寫位. 爲0時表示這樣的頁只能讀取, 爲1時可讀可寫
  • US 是用戶/管理位. 爲1時, 容許全部特權級別的程序訪問; 爲0時, 只容許特權級別爲0, 1和2的程序訪問.
  • PWT(Page-level Write-Through) 是頁級通寫位, 和高速緩存有關. "通寫"是處理器高速緩存的一種工做方式, 這一位用來間接決定是否採用此種方式來改善頁面的訪問效率.
  • PCD(Page-level Cache Disable)是頁級高速緩存禁止位, 用來間接決定該表項所指向的那個頁是否使用高速緩存策略.
  • A 是訪問位. 該位由處理器固件設置, 用來指示此表項所指向的頁是否被訪問過.
  • D(Dirty) 是髒位. 該位由處理器固件設置, 用來指示此表項所指向的頁是否寫過數據
  • PAT(Page Attribute Table) 頁屬性表支持位. 此位涉及更復雜的分頁系統, 和頁高速緩存有關, 能夠不予理會, 在普通的4KB分頁機制中, 處理器建議將其置0.
  • G 是全局位. 用來指示該表項所指向的頁是否爲全局性質的. 若是頁是全局的, 那麼, 它將在高速緩存中一直保存(也就意味着地址轉換速度會很快). 由於頁高速緩存容量有限, 只能存放頻繁使用的那些表項. 並且, 當因任務切換等緣由改變CR3寄存器的內容時, 整個頁高速緩存的內容都會被刷新.
  • AVL位卑處理器忽略, 軟件可使用.

CR3(PDBR)和開分頁機制

控制寄存器CR3, 也就是頁目錄表基地址寄存器PDBR, 該寄存器如上圖所示.

因爲頁目錄表必須位於一個天然頁內(4KB對齊), 故其物理地址的低12位是全0. 低12位除了PCD和PWT外, 都沒有使用. 這兩位用於控制頁目錄的高速緩存特性, 參見上面解釋.

控制寄存器CR0的最高位PG位, 用於開啓分頁或者關閉頁功能. 當該位清0時, 頁功能關閉, 從段部件來的線性地址就是物理地址. 當它置位時, 頁功能開啓. 只能在保護模式下才能開啓分頁功能, 當PE位清0時(實模式), 設置PG位將致使處理器產生一個異常中斷.

相關文章
相關標籤/搜索