虛擬內存是什么意思 - 虛擬內存原理與工作方式
虛擬內存
盡管基址寄存器和變址寄存器用來創建地址空間的抽象,但是這有一個其他的問題需要解決:管理軟件的膨脹(managing bloatware)。雖然內存的大小增長迅速,但是軟件的大小增長的要比內存還要快。在 1980 年的時候,許多大學用一臺 4 MB 的 VAX 計算機運行分時操作系統,供十幾個用戶同時運行。現在微軟公司推薦的 64 位 Windows 8 系統至少需要 2 GB 內存,而許多多媒體的潮流則進一步推動了對內存的需求。
這一發展的結果是,需要運行的程序往往大到內存無法容納,而且必然需要系統能夠支持多個程序同時運行,即使內存可以滿足其中單獨一個程序的需求,但是從總體上來看內存仍然滿足不了日益增長的軟件的需求(感覺和xxx和xxx 的矛盾很相似)。而交換技術并不是一個很有效的方案,在一些中小應用程序尚可使用交換,如果應用程序過大,難道還要每次交換幾 GB 的內存?這顯然是不合適的,一個典型的 SATA 磁盤的峰值傳輸速度高達幾百兆/秒,這意味著需要好幾秒才能換出或者換入一個 1 GB 的程序。
SATA(Serial ATA)硬盤,又稱串口硬盤,是未來 PC 機硬盤的趨勢,已基本取代了傳統的 PATA 硬盤。
那么還有沒有一種有效的方式來應對呢?有,那就是使用 虛擬內存(virtual memory),虛擬內存的基本思想是,每個程序都有自己的地址空間,這個地址空間被劃分為多個稱為頁面(page)的塊。每一頁都是連續的地址范圍。這些頁被映射到物理內存,但并不是所有的頁都必須在內存中才能運行程序。當程序引用到一部分在物理內存中的地址空間時,硬件會立刻執行必要的映射。當程序引用到一部分不在物理內存中的地址空間時,由操作系統負責將缺失的部分裝入物理內存并重新執行失敗的指令。
在某種意義上來說,虛擬地址是對基址寄存器和變址寄存器的一種概述。8088 有分離的基址寄存器(但不是變址寄存器)用于放入 text 和 data 。
使用虛擬內存,可以將整個地址空間以很小的單位映射到物理內存中,而不是僅僅針對 text 和 data 區進行重定位。下面我們會探討虛擬內存是如何實現的。
虛擬內存很適合在多道程序設計系統中使用,許多程序的片段同時保存在內存中,當一個程序等待它的一部分讀入內存時,可以把 CPU 交給另一個進程使用。
分頁
大部分使用虛擬內存的系統中都會使用一種 分頁(paging) 技術。在任何一臺計算機上,程序會引用使用一組內存地址。當程序執行
MOV REG,1000
這條指令時,它會把內存地址為 1000 的內存單元的內容復制到 REG 中(或者相反,這取決于計算機)。地址可以通過索引、基址寄存器、段寄存器或其他方式產生。
這些程序生成的地址被稱為 虛擬地址(virtual addresses) 并形成虛擬地址空間(virtual address space),在沒有虛擬內存的計算機上,系統直接將虛擬地址送到內存中線上,讀寫操作都使用同樣地址的物理內存。在使用虛擬內存時,虛擬地址不會直接發送到內存總線上。相反,會使用 MMU(Memory Management Unit) 內存管理單元把虛擬地址映射為物理內存地址,像下圖這樣
下面這幅圖展示了這種映射是如何工作的
頁表給出虛擬地址與物理內存地址之間的映射關系。每一頁起始于 4096 的倍數位置,結束于 4095 的位置,所以 4K 到 8K 實際為 4096 - 8191 ,8K - 12K 就是 8192 - 12287
在這個例子中,我們可能有一個 16 位地址的計算機,地址從 0 - 64 K - 1,這些是虛擬地址。然而只有 32 KB 的物理地址。所以雖然可以編寫 64 KB 的程序,但是程序無法全部調入內存運行,在磁盤上必須有一個最多 64 KB 的程序核心映像的完整副本,以保證程序片段在需要時被調入內存。
存在映射的頁如何映射
虛擬地址空間由固定大小的單元組成,這種固定大小的單元稱為 頁(pages)。而相對的,物理內存中也有固定大小的物理單元,稱為 頁框(page frames)。頁和頁框的大小一樣。在上面這個例子中,頁的大小為 4KB ,但是實際的使用過程中頁的大小范圍可能是 512 字節 - 1G 字節的大小。對應于 64 KB 的虛擬地址空間和 32 KB 的物理內存,可得到 16 個虛擬頁面和 8 個頁框。RAM 和磁盤之間的交換總是以整個頁為單元進行交換的。
程序試圖訪問地址時,例如執行下面這條指令
MOV REG, 0
會將虛擬地址 0 送到 MMU。MMU 看到虛擬地址落在頁面 0 (0 - 4095),根據其映射結果,這一頁面對應的頁框 2 (8192 - 12287),因此 MMU 把地址變換為 8192 ,并把地址 8192 送到總線上。內存對 MMU 一無所知,它只看到一個對 8192 地址的讀寫請求并執行它。MMU 從而有效的把所有虛擬地址 0 - 4095 映射到了 8192 - 12287 的物理地址。同樣的,指令
MOV REG, 8192
也被有效的轉換為
MOV REG, 24576
虛擬地址 8192(在虛擬頁 2 中)被映射到物理地址 24576(在物理頁框 6 中)上。
通過恰當的設置 MMU,可以把 16 個虛擬頁面映射到 8 個頁框中的任何一個。但是這并沒有解決虛擬地址空間比物理內存大的問題。
上圖中有 8 個物理頁框,于是只有 8 個虛擬頁被映射到了物理內存中,在上圖中用 X 號表示的其他頁面沒有被映射。在實際的硬件中,會使用一個 在/不在(Present/absent bit)位記錄頁面在內存中的實際存在情況。
未映射的頁如何映射
當程序訪問一個未映射的頁面,如執行指令
MOV REG, 32780
將會發生什么情況呢?虛擬頁面 8 (從 32768 開始)的第 12 個字節所對應的物理地址是什么?MMU 注意到該頁面沒有被映射(在圖中用 X 號表示),于是 CPU 會陷入(trap)到操作系統中。這個陷入稱為 缺頁中斷(page fault) 或者是 缺頁錯誤。操作系統會選擇一個很少使用的頁并把它的內容寫入磁盤(如果它不在磁盤上)。隨后把需要訪問的頁面讀到剛才回收的頁框中,修改映射關系,然后重新啟動引起陷入的指令。有點不太好理解,舉個例子來看一下。
例如,如果操作系統決定放棄頁框 1,那么它將把虛擬機頁面 8 裝入物理地址 4096,并對 MMU 映射做兩處修改。首先,它要將虛擬頁中的 1 表項標記為未映射,使以后任何對虛擬地址 4096 - 8191 的訪問都將導致陷入。隨后把虛擬頁面 8 的表項的叉號改為 1,因此在引起陷阱的指令重新啟動時,它將把虛擬地址 32780 映射為物理地址(4096 + 12)。
下面查看一下 MMU 的內部構造以便了解它們是如何工作的,以及了解為什么我們選用的頁大小都是 2 的整數次冪。下圖我們可以看到一個虛擬地址的例子
虛擬地址 8196 (二進制 0010000000000100)用上面的頁表映射圖所示的 MMU 映射機制進行映射,輸入的 16 位虛擬地址被分為 4 位的頁號和 12 位的偏移量。4 位的頁號可以表示 16 個頁面,12 位的偏移可以為一頁內的全部 4096 個字節。
可用頁號作為頁表(page table) 的索引,以得出對應于該虛擬頁面的頁框號。如果在/不在位則是 0 ,則引起一個操作系統陷入。如果該位是 1,則將在頁表中查到的頁框號復制到輸出寄存器的高 3 位中,再加上輸入虛擬地址中的低 12 位偏移量。如此就構成了 15 位的物理地址。輸出寄存器的內容隨即被作為物理地址送到總線。
頁表
在上面這個簡單的例子中,虛擬地址到物理地址的映射可以總結如下:虛擬地址被分為虛擬頁號(高位部分)和偏移量(低位部分)。例如,對于 16 位地址和 4 KB 的頁面大小,高 4 位可以指定 16 個虛擬頁面中的一頁,而低 12 位接著確定了所選頁面中的偏移量(0-4095)。
虛擬頁號可作為頁表的索引用來找到虛擬頁中的內容。由頁表項可以找到頁框號(如果有的話)。然后把頁框號拼接到偏移量的高位端,以替換掉虛擬頁號,形成物理地址。
因此,頁表的目的是把虛擬頁映射到頁框中。從數學上說,頁表是一個函數,它的參數是虛擬頁號,結果是物理頁框號。
通過這個函數可以把虛擬地址中的虛擬頁轉換為頁框,從而形成物理地址。
頁表項的結構
下面我們探討一下頁表項的具體結構,上面你知道了頁表項的大致構成,是由頁框號和在/不在位構成的,現在我們來具體探討一下頁表項的構成
頁表項的結構是與機器相關的,但是不同機器上的頁表項大致相同。上面是一個頁表項的構成,不同計算機的頁表項可能不同,但是一般來說都是 32 位的。頁表項中最重要的字段就是頁框號(Page frame number)。畢竟,頁表到頁框最重要的一步操作就是要把此值映射過去。下一個比較重要的就是在/不在位,如果此位上的值是 1,那么頁表項是有效的并且能夠被使用。如果此值是 0 的話,則表示該頁表項對應的虛擬頁面不在內存中,訪問該頁面會引起一個缺頁異常(page fault)。
保護位(Protection) 告訴我們哪一種訪問是允許的,啥意思呢?最簡單的表示形式是這個域只有一位,0 表示可讀可寫,1 表示的是只讀。
修改位(Modified) 和 訪問位(Referenced) 會跟蹤頁面的使用情況。當一個頁面被寫入時,硬件會自動的設置修改位。修改位在頁面重新分配頁框時很有用。如果一個頁面已經被修改過(即它是 臟 的),則必須把它寫回磁盤。如果一個頁面沒有被修改過(即它是 干凈的),那么重新分配時這個頁框會被直接丟棄,因為磁盤上的副本仍然是有效的。這個位有時也叫做 臟位(dirty bit),因為它反映了頁面的狀態。
訪問位(Referenced) 在頁面被訪問時被設置,不管是讀還是寫。這個值能夠幫助操作系統在發生缺頁中斷時選擇要淘汰的頁。不再使用的頁要比正在使用的頁更適合被淘汰。這個位在后面要討論的頁面置換算法中作用很大。
最后一位用于禁止該頁面被高速緩存,這個功能對于映射到設備寄存器還是內存中起到了關鍵作用。通過這一位可以禁用高速緩存。具有獨立的 I/O 空間而不是用內存映射 I/O 的機器來說,并不需要這一位。
在深入討論下面問題之前,需要強調一下:虛擬內存本質上是用來創造一個地址空間的抽象,可以把它理解成為進程是對 CPU 的抽象,虛擬內存的實現,本質是將虛擬地址空間分解成頁,并將每一項映射到物理內存的某個頁框。因為我們的重點是如何管理這個虛擬內存的抽象。
加速分頁過程
到現在我們已經虛擬內存(virtual memory) 和 分頁(paging) 的基礎,現在我們可以把目光放在具體的實現上面了。在任何帶有分頁的系統中,都會需要面臨下面這兩個主要問題:
- 虛擬地址到物理地址的映射速度必須要快
- 如果虛擬地址空間足夠大,那么頁表也會足夠大
第一個問題是由于每次訪問內存都需要進行虛擬地址到物理地址的映射,所有的指令最終都來自于內存,并且很多指令也會訪問內存中的操作數。
操作數:操作數是計算機指令中的一個組成部分,它規定了指令中進行數字運算的量 。操作數指出指令執行的操作所需要數據的來源。操作數是匯編指令的一個字段。比如,MOV、ADD 等。
因此,每條指令可能會多次訪問頁表,如果執行一條指令需要 1 ns,那么頁表查詢需要在 0.2 ns 之內完成,以避免映射成為一個主要性能瓶頸。
第二個問題是所有的現代操作系統都會使用至少 32 位的虛擬地址,并且 64 位正在變得越來越普遍。假設頁大小為 4 KB,32 位的地址空間將近有 100 萬頁,而 64 位地址空間簡直多到無法想象。
對大而且快速的頁映射的需要成為構建計算機的一個非常重要的約束。就像上面頁表中的圖一樣,每一個表項對應一個虛擬頁面,虛擬頁號作為索引。在啟動一個進程時,操作系統會把保存在內存中進程頁表讀副本放入寄存器中。
最后一句話是不是不好理解?還記得頁表是什么嗎?它是虛擬地址到內存地址的映射頁表。頁表是虛擬地址轉換的關鍵組成部分,它是訪問內存中數據所必需的。在進程啟動時,執行很多次虛擬地址到物理地址的轉換,會把物理地址的副本從內存中讀入到寄存器中,再執行這一轉換過程。
所以,在進程的運行過程中,不必再為頁表而訪問內存。使用這種方法的優勢是簡單而且映射過程中不需要訪問內存。缺點是 頁表太大時,代價高昂,而且每次上下文切換的時候都必須裝載整個頁表,這樣會造成性能的降低。鑒于此,我們討論一下加速分頁機制和處理大的虛擬地址空間的實現方案
轉換檢測緩沖區
我們首先先來一起探討一下加速分頁的問題。大部分優化方案都是從內存中的頁表開始的。這種設計對效率有著巨大的影響。考慮一下,例如,假設一條 1 字節的指令要把一個寄存器中的數據復制到另一個寄存器。在不分頁的情況下,這條指令只訪問一次內存,即從內存取出指令。有了分頁機制后,會因為要訪問頁表而需要更多的內存訪問。由于執行速度通常被 CPU 從內存中取指令和數據的速度所限制,這樣的話,兩次訪問才能實現一次的訪問效果,所以內存訪問的性能會下降一半。在這種情況下,根本不會采用分頁機制。
什么是 1 字節的指令?我們以 8085 微處理器為例來說明一下,在 8085 微處理中,一共有 3 種字節指令,它們分別是 1-byte(1 字節)、2-byte(2 字節)、3-byte(3 字節),我們分別來說一下
1-byte:1 字節的操作數和操作碼共同以 1 字節表示;操作數是內部寄存器,并被編碼到指令中;指令需要一個存儲位置來將單個寄存器存儲在存儲位置中。沒有操作數的指令也是 1-byte 指令。
例如:MOV B,C 、LDAX B、NOP、HLT(這塊不明白的讀者可以自行查閱)
2-byte: 2 字節包括:第一個字節指定的操作碼;第二個字節指定操作數;指令需要兩個存儲器位置才能存儲在存儲器中。
例如 MVI B, 26 H、IN 56 H
3-byte: 在 3 字節指令中,第一個字節指定操作碼;后面兩個字節指定 16 位的地址;第二個字節保存低位地址;第三個字節保存 高位地址。指令需要三個存儲器位置才能將單個字節存儲在存儲器中。
例如 LDA 2050 H、JMP 2085 H
大多數程序總是對少量頁面進行多次訪問,而不是對大量頁面進行少量訪問。因此,只有很少的頁面能夠被再次訪問,而其他的頁表項很少被訪問。
頁表項一般也被稱為 Page Table Entry(PTE)。
基于這種設想,提出了一種方案,即從硬件方面來解決這個問題,為計算機設置一個小型的硬件設備,能夠將虛擬地址直接映射到物理地址,而不必再訪問頁表。這種設備被稱為轉換檢測緩沖區(Translation Lookaside Buffer, TLB),有時又被稱為 相聯存儲器(associate memory) 。