
作者:天津九安特機電工程有限公司 來(lái)源: 天津九安特機電工程有限公司 日期:2026-05-05 09:26:52
程序自身并不需要關(guān)心自己的操作數據及代碼存在哪,并且對程序來(lái)說(shuō),系統虛擬內存看上去是筆記連續且獨占的。當然事實(shí)肯定不是(shi)內存如此,而這背后就是操作操作系統的功勞 —— 內存虛擬化。本篇文章就介紹操作系統是系統虛擬如何實(shí)現虛擬內存系統的。
操作系統提供了一個(gè)易用的筆記物理內存抽象:地址空間。地址ヾ(′▽?zhuān)??空間是內存運行的程序看到的系統中的內存。
一個(gè)進(jìn)程的操作地址??空間包(′▽?zhuān)?含運行的程序的所有內存狀態(tài)。每次內存引用時(shí),系統虛擬硬件??都會(huì )進(jìn)行地址轉換,筆記將應用程序的內存內存引用重定向到內存中實(shí)際的位置。
為了完成地址轉換,操作每個(gè) CPU 需要兩個(gè)硬件寄存器:基址 (base) 寄存器和界限 (bound) 寄存器。系統虛擬程序的筆記起始地址存放在基址寄存器。進(jìn)程產(chǎn)生的所有內存引用,都會(huì )被處??理器通過(guò)以下方式轉換成物理地址:
界限寄存器的作用在于,確??保了進(jìn)程產(chǎn)生的所有地址都在進(jìn)程的地址 “界限” 中。
操作系統和硬件??支??持結合,實(shí)現了虛擬內存,而為了實(shí)現虛擬內存,操作系統所需要做的工作如下:
為了解決連續內存的浪費問(wèn)題,ヽ(′▽?zhuān)?ノ操作系統引入了分段??。
具體來(lái)說(shuō),在 MMU 中引入不止一個(gè)基址和界限寄存器對,而是給地址空間內的每個(gè)邏輯段一對。一個(gè)段只是地址空間里的一個(gè)連續定長(cháng)的區域,在典型??的地址空間里有 3 個(gè)邏輯不同的段:代碼、棧和堆。
分(′?_?`)段機制使得操作系統能夠將不同的段放入不同的(′▽?zhuān)?)物理內存區域,從而避免了虛擬地址空間中的未使用部分占用物理內存。如下圖所示:
而如何從一個(gè)虛擬地址中識別出對應的段是哪一個(gè),主要有兩個(gè)方法:
分段帶來(lái)一些新的問(wèn)題。
第一個(gè)是段寄存器的值必須被保存和恢復。每個(gè)進(jìn)程都有自己獨立的虛擬地址空間,??操作系統必須在進(jìn)程運行前,確保這些寄存器被正確的賦值。
第二個(gè)也是更重要的(de)問(wèn)題是分段會(huì )帶來(lái)外部碎片??臻e空間被分割成不同大小的小塊,成為碎片,后續的請求可能會(huì )失敗,因為沒(méi)有一塊足夠大的連續空閑空間,即使這時(shí)總的空閑空間超出了請求的大小。
解決外部??碎片??的一種方法(′?`*)是緊湊物理內存,重新安排原有的段,但內存緊湊成本很(hen)高;另一種簡(jiǎn)單的方法是使用空閑列表(free-list)管理算法,試圖保(O_O)留大額內存用于分配。目前已經(jīng)有數百種方法,包括經(jīng)典算法:
還有一些改進(jìn)策略:
然而不管(guan)算法多么精妙,外部碎片仍然存在,無(wú)法完全消除。唯一真正解決的辦??法就是完全避免這個(gè)問(wèn)題,永遠不要分配不同(′ω`)大小的內存塊,這也是分頁(yè)被引入的原因??。
分頁(yè)不是將一個(gè)進(jìn)程的地址空間分割成幾個(gè)不同長(cháng)度的邏輯段 (即代碼、堆、段),而是分割成固定大小的單元,每個(gè)單元稱(chēng)為一頁(yè)。相應的,我們把物理內存看成是定長(cháng)槽塊的陣列,叫做頁(yè)幀。每個(gè)頁(yè)幀包含一個(gè)虛擬內存頁(yè)。
操作系統為??每個(gè)進(jìn)程保存一個(gè)數據結構,稱(chēng)為頁(yè)表。主要(yao)用來(lái)為地址空間的每個(gè)( ?ヮ?)虛擬頁(yè)面保存地址轉換,從而讓我們知道每個(gè)(ge)頁(yè)在物理內存中的位置。
虛擬地址分成兩個(gè)組件:虛擬頁(yè)號(VPN)和頁(yè)內的偏移量(offset)
通過(guò)虛擬頁(yè)號,我們現在可以檢索頁(yè)表,找到虛擬頁(yè)所( ???)在的物理頁(yè)面。因此,我們可以通過(guò)用物理幀號替??換虛擬頁(yè)號來(lái)轉換此虛擬地址,然后將載入發(fā)送給物理內存。偏移量保持不變ヾ(′?`)?,因為偏移量只是告訴我們頁(yè)面中的哪個(gè)字節是我們想要的。如下圖所示:
簡(jiǎn)言之,頁(yè)表就是一種數據結構,用于將虛擬地址 (或者(zhe)實(shí)際上,是虛擬頁(yè)號) 映射到物理地址 (物理幀號)。因此任何數(′?_?`)據結構都可以采用,最簡(jiǎn)單的形式成為線(xiàn)性頁(yè)表,就是一個(gè)數組。??操作系統通過(guò)虛擬頁(yè)號 (VPN) 檢索該數組,并在該索引處查找頁(yè)表項 (PTE) ,以找到期望的物理幀號 (PFN)??。
分頁(yè)雖然看起來(lái)是內存虛擬化需求的一個(gè)(ge)很好的解決方(⊙_⊙)案,但這兩個(gè)關(guān)鍵問(wèn)題必須先克服。
為了解決頁(yè)表(′?`*)(biao)內存開(kāi)銷(xiāo)過(guò)多的問(wèn)??題,M??ultics 的創(chuàng )造者提出了分頁(yè)和分段結合ヽ(′▽?zhuān)?/的想法。解決方法是,不是為進(jìn)程的整個(gè)地址空間提供單個(gè)頁(yè)表,而是ヽ(′ー`)ノ為??每個(gè)邏輯分擔提供一個(gè)頁(yè)表。
在分段中,有一個(gè)基址寄存器用來(lái)存放每個(gè)段在物理內存中的位置,還有一個(gè)界限寄存器用來(lái)存放該段的大小。在這里依然使用這些結構,不過(guò),基址不是指向段本身,而是保存該段的頁(yè)表的物理地址;界限寄存器用來(lái)指示頁(yè)表的結尾(即它有多少有效位)。
這種雜合方案的關(guān)鍵區別在于,每個(gè)分段都有界限寄存器,每個(gè)界限寄存器保存了段中最大有效頁(yè)的值。例如,如果代碼段使用前3個(gè)頁(yè),則代碼段頁(yè)表將只有3個(gè)項分配給它,并且界限寄存器將被設置為3。與線(xiàn)性頁(yè)表相比,雜合方法實(shí)現了顯著(zhù)的內存節省,棧和堆之間未分配的頁(yè)不再占用頁(yè)表中的空間 (僅將其(qi)標記為無(wú)效)。
而這種方法的弊端在于,一是它仍然要求使用分段,如果有一個(gè)大而稀疏的堆,仍然可能導致大量的頁(yè)表浪費;二是外部碎片再次出現,??盡管大部分內存是以頁(yè)表大小單位管理的,但頁(yè)表現在可以是任意大小 (PTE 的倍數)。
多級頁(yè)表也是用來(lái)解決頁(yè)??表占用太多內存的問(wèn)題,去掉頁(yè)表中的所有無(wú)效區域,而不是將它們全部保留在內存中。多級頁(yè)表將線(xiàn)性頁(yè)表變成了樹(shù)。
首先,將頁(yè)表分成頁(yè)大小的單元;然后,如??果整頁(yè)的(⊙_⊙)頁(yè)表項 (PTE) 無(wú)效,就完全不分配該頁(yè)的頁(yè)表。為了追蹤頁(yè)表的也是否有效 (以及如果有效,它在內存中的位置),使用了名為頁(yè)目錄的新結構。頁(yè)目錄可以告訴你頁(yè)表??的頁(yè)在哪里,或者頁(yè)表的整個(gè)頁(yè)不包含有效頁(yè)。
下面的圖展示了一個(gè)例子(′?`),左邊是經(jīng)典的線(xiàn)性頁(yè)表,??即使地址空間的大部分中間區域無(wú)效 (即頁(yè)表的中間兩頁(yè)),我們仍然需要為這些區域(???)分配??頁(yè)表空間;右邊是一個(gè)多級頁(yè)表,頁(yè)目錄僅將這些區域分配頁(yè)表空間 (即第一個(gè)和最后一個(gè)),頁(yè)表的這兩(liang)頁(yè)就駐留在內存中。因此,我們可以形象地看到多級頁(yè)表的工作方式:只是讓線(xiàn)性頁(yè)表的一部分消失 (釋放這些幀用作其他用途),并用頁(yè)目錄記錄頁(yè)表的哪些頁(yè)也(′?`*)被分配。
在一個(gè)簡(jiǎn)單的兩(liang)級頁(yè)表(′_ゝ`)中,頁(yè)??目錄為每頁(yè)頁(yè)表包含了一項。由多個(gè)頁(yè)目錄項 (PDE) 組成,PDE 至少擁有有效???位 (valid bit) 和頁(yè)幀號 (PFN),類(lèi)似于 PTE。但這(′▽?zhuān)?個(gè)有效位的含義稍ヽ(′ー`)ノ有不同:如果 PD(//ω//)E 項是有效的,則意味著(zhù)該項指向的頁(yè)表 (通過(guò) PTE) 中至少有一(???)頁(yè)是有效的,即在該 PDE 所指向的頁(yè)中(zhong),至少一??個(gè) PTE,其有效位被設置為 1。如果 PDE 項無(wú)效,則 PDE 的其余部分沒(méi)有定義。
多級頁(yè)表分配的頁(yè)表空間,與你正在使用的地址空間內存量成比例,因此通常很緊湊,并且支持稀疏的地址??空間。
如果仔(′ω`*)細構建,頁(yè)表的每個(gè)部分都可以整齊的放入一頁(yè)中,從而更容易管理內存。有了多級頁(yè)表,我們增加了一個(gè)間接層,使用了頁(yè)目錄,指向(°□°)頁(yè)表的各個(gè)部分,這種間接方式,讓我們能夠將頁(yè)表頁(yè)放在物理內存的任何地方。
多級頁(yè)表是有成本的,在 TLB 未命中時(shí),需要從內存加載兩次ヽ(′?`)ノ,才能(neng)從頁(yè)表中獲取正確的地址轉換信息 (一次用于頁(yè)目錄,另一次用于 PTE 本身),而用線(xiàn)(xian)性頁(yè)表只??需要一次加載。
另一個(gè)明顯的缺點(diǎn)是復雜性。無(wú)論是硬件還是操作??系統來(lái)處理頁(yè)表查找,這樣做無(wú)疑都比簡(jiǎn)單的線(xiàn)性(xing)頁(yè)表查找更復雜。
為了解決分頁(yè)所帶來(lái)的??額外內存訪(fǎng)問(wèn)的問(wèn)題,操作系統需要一些額外的幫助,因此引入了地址轉換旁路緩沖寄存器 (TLB),就是頻繁發(fā)生的虛擬到物理地址轉換的硬件緩存。
如果有,我們就有了 TLB 命中,意味著(zhù) TLB 有該頁(yè)的轉換映射,就可以從相關(guān)的 TLB 項中取出(chu)頁(yè)診號 (PFN) 與原來(lái)虛擬地址中的偏移量組合成期望的物理地址;
如果沒(méi)有 (TLB 未命中),在不(bu)同的系統中表現不一樣:
TLB 中包含的虛擬到物理地址映射只對當前進(jìn)程有效,對其他進(jìn)程是沒(méi)有意義的。所以在上下文切換時(shí),TLB 的管理有兩種方法。(???)
如果是軟件管理 TLB 的系統,可以在??發(fā)生上下文切換時(shí),通過(guò)一條顯式指令來(lái)完成;如果是硬件管理 TLB 系統,則可以在頁(yè)表基址寄存器內容發(fā)生(sheng)變化時(shí)清空 TLB。不論哪種情況??,情況操作都是把全部有效位置為 0,本質(zhì)上清空了 TLB。
但該方法有一定開(kāi)銷(xiāo):每次?進(jìn)程運行,當它訪(fǎng)問(wèn)數據和ˉ\_(ツ)_/ˉ代碼頁(yè)時(shí),都會(huì )觸發(fā) TLB 未命中,如果操作系統頻繁切換進(jìn)程,這種開(kāi)銷(xiāo)會(huì )很高。
增加硬件(jian)支持,實(shí)現跨上下文切(qie)換的 TLB 共享。比如有的系統在 TLB 中添加一個(gè)地址空間標識符 (ASID),可以把 ASID 看做是進(jìn)程標識符,但通常比 PID 位數少一位。TLB 因此可以同時(shí)緩存不同進(jìn)程的地址空間映射,沒(méi)有任何沖突。
為了支持更大的地址空間,操作系統需要把當前沒(méi)有在用的那部分地址空間找個(gè)地??方存儲起來(lái)。硬盤(pán)通常能夠滿(mǎn)足這個(gè)需求,在我們的(′ω`)存儲層級結構中,大而慢??的硬盤(pán)位于底層,內存之上。增加交換空間(◎_◎;)讓(′?`)操作系統為多個(gè)并發(fā)運行的進(jìn)程都提供巨大地址空間的假象。
在硬盤(pán)上開(kāi)辟一部分空間用于物理??頁(yè)的移入和移出,在操作系統中這樣的空??間稱(chēng)為交換空間,因為我們將內存中的頁(yè)交換到其中,并在需要的時(shí)候又交換回去。因此,我們會(huì )假設操作系統能(neng)夠以頁(yè)為大小為單元讀取或者寫(xiě)入交換空間,為了達到這個(gè)目的。
訪(fǎng)問(wèn)??不在物理內存中的頁(yè),這種行為通常被稱(chēng)為頁(yè)錯誤(°ロ°) !。這時(shí) “頁(yè)錯誤處理程序” 被執行,處理頁(yè)錯誤。
處理頁(yè)錯誤的流程:
當硬盤(pán) I/Oヾ(′▽?zhuān)?? 完成時(shí),操作系統會(huì )更新頁(yè)表,將此頁(yè)標記為存在,更新頁(yè)表項的 PFN 字段以記錄新獲取頁(yè)的內存位置,并重試指令。下一次重新訪(fǎng)問(wèn) TLB 還是未命中,然而這次因為頁(yè)(′▽?zhuān)?在內存中,因此會(huì )將頁(yè)表中的地址更新到 TLB 中。
最后的重試操作會(huì )在 TLB 中找到轉換映射,從已轉換的內(′▽?zhuān)?)存物理地址,獲取所需的數據或指令。
為了保證有(you)少量的空閑內存,大多數操作系統會(huì )設置高水位線(xiàn) (HW) 和低水位線(xiàn) (LW)。
原??理是:當操作系統發(fā)現??有少于 LW 個(gè)頁(yè)可用時(shí),后臺負責釋放內存的線(xiàn)程會(huì )開(kāi)始運行,直到有 HW 個(gè)可用的物理頁(yè)。這個(gè)后臺線(xiàn)程有時(shí)稱(chēng)為交換守護進(jìn)程 (swap daemon) 或頁(yè)守護進(jìn)程 (page daemon),然后會(huì )進(jìn)入休眠狀態(tài)。
最優(yōu)替換策略能達到總體未命中數量最少,即替換內存中在最遠將來(lái)才會(huì )被訪(fǎng)問(wèn)到的頁(yè),可以達到緩存未命中率最低。但很難實(shí)現。
F(//ω//)I(′_`)FO 策略,即頁(yè)在進(jìn)(jin)入系統時(shí),簡(jiǎn)單地放入一個(gè)隊列,當發(fā)生替換時(shí),隊列尾部的頁(yè)被踢出。
FIFO 有個(gè)很大的優(yōu)勢:實(shí)現??相當簡(jiǎn)單。(╯°□°)╯但其?根本無(wú)法確定頁(yè)的重要性,即使頁(yè) 0 已被多次訪(fǎng)問(wèn), FIF??O 仍然會(huì )將其踢出。且 FIFO 會(huì )引起 Belady 異常。
LRU 策略是替換最近最少使用的頁(yè)。
LRU 目前看來(lái)優(yōu)于 FIFO 策略及隨機策略,但隨著(zhù)系統中頁(yè)的數量的增長(cháng),掃描所有頁(yè)的(???)時(shí)間字段只是為了找到最精確最少使用的頁(yè),這個(gè)代價(jià)太大。
Clock 算法是近似 LRU 的一種算法,也是許多現代系統的做法。該算法需要硬件增加一個(gè)使用位。
過(guò)程:
考慮到內存中的頁(yè)是否(′?ω?`)被修改,硬件增加一個(gè)修改位。每次寫(xiě)入頁(yè)時(shí)都會(huì )設置此位,因此可以將(jiang)其合并到頁(yè)面替換算法中。如果頁(yè)已被修改并因此變臟,( ???)則提出就必須將它寫(xiě)回磁盤(pán),這很昂貴;如果沒(méi)有被修改,踢出就沒(méi)有成本。因此,一些虛擬系統更傾向于踢出干凈頁(yè),而不是臟頁(yè)。