讀懂操作系統之虛擬內存(一)

前言

由於個人對虛擬內存這塊特別感興趣,所以就直接暫且跳過其他,接下來將通過幾篇文章進行詳細講解,當然其他基礎內容後續在我進行相應整體學習後也會同步輸出文章,比如操作系統概念、程序鏈接、進程管理、頁面置換算法、流水線、浮點指令、內存管理、磁盤管理等內容。不管周遭的環境如何,畢竟還很菜,堅持每天讓自己進步一點點,放下暫時的焦慮,不如專註於眼前的學習,跟着我一起學習操作系統吧。

虛擬存儲器設計初衷

緩存從高到低,主存可作為磁盤的緩存,我們將這項技術稱作為虛擬存儲器,基於歷史觀點,構造虛擬存儲器的主要出於兩個目的,其一是允許雲計算在多個虛擬機之間有效而安全的共享存儲器,其二則是受限的主存容量對程序設計所造成的極大影響。有了虛擬存儲器這樣可確保每個程序只能對劃分給它的那部分主存進行讀寫操作,而主存只需存放程序中的活躍區域。虛擬存儲器實現程序地址空間到物理地址的轉換,通過這種轉換處理加強了各個程序地址之間的保護。若用戶加載多個程序直接到主存,很顯然最終可能會超過主存的容量,如此一來,將程序進行卸載或轉入成為程序員不可推卸的責任,加重了程序員的負擔,那麼怎樣才能將程序員從這種情況中解放出來呢?現代操作系統將主存提供了一種對主存的抽象概念,叫作虛擬內存,虛擬內存是硬件異常,硬件地址翻譯、主存、磁盤文件和內核軟件的完美交互,虛擬內存主要提供了三種能力:【1】將主存看成是一個存儲在磁盤上的地址空間的高速緩存(作為緩存)【2】為每個進程提供一致的地址空間,從而簡化內存管理(作為內存管理)【3】防止每個進程的地址空間被其他進程所破壞(作為內存保護)。

映射原理:CPU生成一個虛擬地址(Virtual Address簡稱VA)來訪問主存,但是在此之前需要將虛擬地址轉換為物理地址,這個過程稱作為地址轉換或地址映射或地址翻譯,為進行此操作需要CPU硬件和操作系統合作,通過內存管理單元(Memory Management Unit)上的地址翻譯硬件,利用存儲在主存上的查詢表來翻譯虛擬地址,該表的內容由操作系統管理。

頁表地址翻譯

虛擬內存系統將虛擬內存劃分為固定大小的塊,這個塊我們稱作為虛擬頁(Virtual Page簡稱VP),同理將物理內存劃分為物理頁(Physical Page簡稱PP),也叫頁幀(Page Frame)。這裡我們需要明白虛擬地址、虛擬頁、物理地址、物理頁這四者之間的聯繫。如下圖,通過CPU產生虛擬地址訪問主存的物理地址,而虛擬存儲器和物理存儲器分別被劃分成虛擬頁和物理頁,所以結果變成將虛擬頁映射到物理頁。

 

在虛擬存儲器中,地址被劃分為虛頁號(Virtual Page Number簡稱VPN)和虛擬頁頁偏移(Virtual Page Offset簡稱VPO),如下為從虛擬頁到物理頁號(Physical Page Number簡稱PPN)的轉換。物理頁構成物理地址的高位部分,而頁偏移保持不變,構成物理地址的低位部分。頁偏移的位數決定了頁的大小,當然,虛擬頁地址可尋址的頁數與物理地址可尋址的頁數可以不同。

例如具有32位邏輯地址的操作系統,如果每個頁面的大小為4KB,由於上述物理頁號有18位,那麼存儲器物理頁數為2^18,因此,邏輯上最多可以支持1GB的主存,但是實際上虛擬地址空間可支持4GB,如此為解決主存的受限控制。和緩存機制一樣,我們必須有某種機制來判斷一個虛擬頁是否緩存在DRAM中某一個地方,如果是,那麼系統必須確定這個虛擬頁存放在哪個物理頁中,如果未命中,系統必須確定這個虛擬頁存儲在磁盤的哪個位置,然後在物理內存中選擇一個犧牲頁,並將虛擬頁從磁盤複製到DRAM中,然後替換這個犧牲頁。此功能由軟硬件共同提供,包括操作系統軟件、MMU中的地址翻譯硬件、存放在物理內存中的頁表(Page Table簡稱PT)所決定,上述將虛擬頁映射到物理頁就是通過查詢頁表實現,一個頁表是由頁表條目(Page Table Entry簡稱PTE)的數組組成。如下為頁表簡要信息版本:

我們看到上述每個PTE是由一個有效位和一個包含物理頁號或磁盤地址組成,有效位標識虛擬頁是否被緩存在主存中, 若為1則說明該虛擬頁已被緩存在主存中,若為0分為兩種情況,可能是虛擬內存未創建虛擬頁,也有可能是已創建虛擬頁但還未緩存到主存,所以虛擬頁集合由3個子集組成:【1】虛擬內存系統還未分配或未創建、【2】已緩存在物理內存中的已分配頁、【3】未緩存在物理內存中的已分配頁。頁表在主存中的位置由硬件決定,硬件包含一個指向頁表首地址的頁表基址寄存器(Page Table Base Register簡稱PTBR)。簡要翻譯過程如下圖所示

我們進一步得出MMU將虛擬地址映射為物理地址的詳細過程:CPU產生虛擬地址,然後從虛擬地址中得到虛擬頁號,接下來通過將虛擬頁號作為索引去查找頁表,通過得到對應PTE上的有效位來判斷當前虛擬頁是否在主存中,若命中則將對應PTE上的物理頁號和虛擬地址中的虛擬頁偏移進行串聯從而構造出主存中的物理地址,否則未命中(專業名詞稱為「缺頁」),此時MMU將引發缺頁異常,從CPU傳遞到操作系統內核處理缺頁異常處理程序,此時將選擇一個犧牲頁並將對應所缺虛擬頁調入並更新頁表上的PTE,缺頁處理程序再次返回到原來的進程,再次執行缺頁指令,CPU重新將虛擬地址發給MMU,此時虛擬頁已存在物理內存中,所以命中,最終將請求的字返回給處理器。詳細過程如下:

進程的地址空間以及它在主存中可以訪問的所有數據,都由駐存在主存中的頁表所定義,操作系統只是簡單的加載頁表寄存器用來指向它所想激活的進程頁表,而不是保存整個頁表。由於不同進程使用相同的虛擬地址,因此每個進程有各自的頁表,操作系統負責分配物理主存和更新頁表,因為我們通過將頁表分離來保護進程,進而保證不同進程的虛擬地址空間不會發生任何衝突。上述我們已經得知,一個32位邏輯地址空間的系統,每個頁面的大小為4KB(2^12),那麼頁表將包含(2^32/2^12)= 大約100萬個頁表條目即PTE,假設每個條目佔4個位元組,那麼每個進程的頁表將佔用(4*(2^32/2^12))= 4MB的內存。

TLB加快地址翻譯

我們知道每次CPU產生一個虛擬地址就必須通過MMU去查詢頁表從而得到PTE,但是頁表存儲在主存中,因此程序訪存至少需要兩次:第一次訪存獲取物理地址、第二次訪存獲得數據。提高訪問性能的關鍵在於依靠頁表的訪問局部性,當一個轉換的虛擬頁號被使用時,它可能在不久的將來會被再次用到,因為對該頁中字的引用同時具有時間局部性和空間局部性。為了消除這樣的開銷,在MMU中包括一個關於PTE的小的緩存,稱為翻譯後備緩衝器(Translation-Lookasice Buffer簡稱TLB),有些書中被稱為快表,所以TLB存在的目的是:用於記錄最近使用地址的映射信息的高速緩存,從而可以避免每次都訪問頁表。TLB是一個小的、虛擬尋址的緩存,其中每一行都保存着一個由單個PTE組成的塊,TLB大概包含64-1024個PTE。如下圖所示

從虛擬頁號中提取出用於組選擇和行匹配的索引和標記字段,如果TLB有 T= 2^t個組,那麼TLB索引(TLBI)是由VPN的t個最低位組成,而TLB標記(TLBT)是由VPN中剩餘的位組成。上述我們了解到虛擬地址由虛擬頁號和虛擬頁偏移量組成,在這裡再進行補充,虛擬地址由虛擬頁號(VPN)、虛擬頁偏移量(VPO)、TLBI(TLB索引)、TLBT(TLB標記)組成,對於物理地址由物理頁號(PPN)、物理頁偏移量(PPO)、緩衝塊內的位元組偏移量(CO)、高速緩存索引(CI)、高速緩存標記(CT)組成。那麼利用TLB加快地址翻譯的整個大概過程是怎樣的呢?CPU產生一個虛擬地址,MMU從虛擬地址中提取出虛擬頁號,然後從TLB中根據虛擬頁號取出相應的PTE,進而通過物理頁號和虛擬頁偏移構造出物理地址,將其發送到高速緩存/主存,高速緩存/主存將返回的數據返回給CPU(現代操作系統都已有SRAM的高速緩存(一級緩存、二級緩存、三級緩存),因為地址翻譯硬件發生在訪問高速緩存之前,所以我們到底是通過虛擬地址還是物理地址訪問高速緩存呢?大多數都是選擇物理地址訪問高速緩存)。整個過程如下圖:

總結

本節屬於小試牛刀,只是從整體上去分析虛擬內存地址翻譯原理,裏面仍涉及太多細節,比如頁面交換策略、頁表和TLB所包含詳細內容,多級頁表、TLB查找PTE、TLB缺失等,下一節我們分析TLB缺失和頁面交換具體原理。