為了更加有效的管理內(nèi)存并減少出錯(cuò)趣席,現(xiàn)在操作系統(tǒng)提供了一種對(duì)主存的抽象概念螟炫,叫做虛擬內(nèi)存椎镣。虛擬內(nèi)存是計(jì)算機(jī)系統(tǒng)內(nèi)存管理的一種重要技術(shù),它為每個(gè)進(jìn)程提供了一個(gè)大的庶艾、一致的和私有的地址空間秘蛇,從而簡(jiǎn)化了內(nèi)存管理。它總是沉默的然评、自動(dòng)的工作惊科,不需要應(yīng)用程序員去做什么,特別是對(duì) JAVA 這種有自動(dòng)內(nèi)存回收機(jī)制的語言來說煤裙,跟內(nèi)存直接打交道的機(jī)會(huì)就更少了掩完。但是虛擬內(nèi)存系統(tǒng)是一個(gè)設(shè)計(jì)非常巧妙、有意思的系統(tǒng)硼砰,很值得我們?nèi)W(xué)習(xí)且蓬,很多思想可以借鑒。
虛擬內(nèi)存主要提供了以下三個(gè)重要能力:
- 它將主存看成是一個(gè)存儲(chǔ)在磁盤上的地址空間的高速緩存题翰,它高效的使用了主存
- 它為每個(gè)進(jìn)程提供了一致的地址空間恶阴,從而簡(jiǎn)化了內(nèi)存管理
- 它包含了每個(gè)進(jìn)程的地址空間不被其他進(jìn)程破壞
虛擬尋址
計(jì)算機(jī)的主存可以被看成是一個(gè)由 M 個(gè)連續(xù)的字節(jié)大小的單元組成的數(shù)組,每個(gè)字節(jié)都有一個(gè)唯一的編號(hào)被稱為物理地址(Physical Address豹障,PA)冯事。早先的計(jì)算機(jī)都是使用物理尋址,即直接根據(jù)物理地址讀取相應(yīng)的數(shù)據(jù)血公,現(xiàn)在的一些嵌入式系統(tǒng)仍然使用物理尋址方式£墙觯現(xiàn)代的計(jì)算機(jī)都使用的是一種虛擬尋址的尋址方式,如下圖所示累魔。
使用虛擬尋址方式摔笤,CPU 生成一個(gè)虛擬地址(Virtual Address够滑,VA)來訪問主存,這個(gè)虛擬地址在被送到主存之前會(huì)先轉(zhuǎn)換成物理地址籍茧。CPU 芯片上有一個(gè)叫內(nèi)存管理單元(Memory Management Unit版述,MMU)的專用硬件,利用存放在主存中的查詢表來動(dòng)態(tài)翻譯虛擬地址寞冯。
虛擬內(nèi)存組織
和存儲(chǔ)器層次結(jié)構(gòu)中其他緩存一樣渴析,磁盤上的數(shù)據(jù)被分割成大小相同塊,這些塊作為磁盤和主存之間的傳輸單元吮龄。計(jì)算機(jī)系統(tǒng)將虛擬內(nèi)存分割成被稱為虛擬頁(yè)(Virtual Page俭茧,VP)的大小固定的塊來處理此問題,同樣物理內(nèi)存也被分割成物理頁(yè)(Physical Page漓帚,PP)大小的固定塊母债,大小與虛擬頁(yè)一致,物理頁(yè)也被稱為頁(yè)幀尝抖。
虛擬頁(yè)存在三種狀態(tài):
- 未分配的:系統(tǒng)還未分配的頁(yè)毡们,為分配的塊沒有任何數(shù)據(jù)與他們想關(guān)聯(lián)。
- 緩存的:緩存了物理內(nèi)存中的已分配頁(yè)昧辽。
- 未緩存的:未緩存在物理內(nèi)存中的已分配頁(yè)衙熔。
組織結(jié)構(gòu)
在存儲(chǔ)層次結(jié)構(gòu)中,我們知道 DRAM 比 SRAM 慢了大約 10 倍搅荞,而磁盤比 DRAM 慢了大于 10W 倍红氯,因此,DRAM 緩存的不命中要比 SRAM 的緩存不命中代價(jià)大的多咕痛。從磁盤的一個(gè)扇區(qū)讀取第一個(gè)字節(jié)的時(shí)間開銷比起讀取這個(gè)扇區(qū)中連續(xù)字節(jié)要慢大約 10W 倍痢甘。
因?yàn)椴幻写鷥r(jià)較大及訪問第一個(gè)字節(jié)的大開銷,虛擬頁(yè)也往往比較大茉贡,通常是 4KB~2MB塞栅。同時(shí)由于不命中代價(jià)較大,DRAM 緩存策略是全相連的腔丧,因?yàn)橄啾容^不命中的代價(jià)构蹬,搜索的復(fù)雜度是可容忍的。
頁(yè)表
與其他緩存系統(tǒng)一樣悔据,虛擬內(nèi)存系統(tǒng)需要用某種方法來判斷一個(gè)虛擬頁(yè)是否已經(jīng)緩存在 DRAM 中,同時(shí)也需要知道確切的物理頁(yè)位置俗壹。如果未緩存的科汗,還需要知道這個(gè)虛擬頁(yè)對(duì)應(yīng)磁盤的哪個(gè)位置。
在物理內(nèi)存中存放著一個(gè)數(shù)據(jù)結(jié)構(gòu)被稱為頁(yè)表(Page Table绷雏,PT)头滔,頁(yè)表將虛擬頁(yè)映射到物理頁(yè)怖亭。每次地址翻譯的時(shí)候就需要讀取頁(yè)表。頁(yè)表中的每個(gè)條目被稱為頁(yè)表?xiàng)l目(Page Table Entry坤检,PTE)兴猩。
頁(yè)命中
當(dāng) CPU 要讀取包含在 VP2 虛擬頁(yè)中的數(shù)據(jù)時(shí)候,地址翻譯器將虛擬地址作為索引來定位到 PTE2早歇,因?yàn)樵O(shè)置了有效位 1倾芝,那么地址翻譯器就知道 VP2 已經(jīng)緩存在 DRAM 中,然后就可以使用 PTE 中的物理內(nèi)存地址進(jìn)行數(shù)據(jù)讀取箭跳。
缺頁(yè)
在虛擬內(nèi)存系統(tǒng)中晨另,DRAM 緩存不命中被稱為缺頁(yè)。
例如當(dāng) CPU 需要讀取包含在 VP3 虛擬頁(yè)中的數(shù)據(jù)時(shí)候谱姓,由于有效位為 0借尿,所以可以判定 VP3 未被緩存到 DRAM 中。也是就觸發(fā)了缺頁(yè)異常屉来,缺頁(yè)異常調(diào)用內(nèi)核的缺頁(yè)異常處理程序路翻,如果 DRAM 已滿,該程序會(huì)選擇一個(gè)犧牲頁(yè)茄靠,例如下圖將 VP4 作為犧牲頁(yè)茂契。如果 VP4 已經(jīng)被修改了,那么內(nèi)核會(huì)將它復(fù)制到磁盤嘹黔。
接下來账嚎,內(nèi)核會(huì)從磁盤復(fù)制 VP3 到內(nèi)存中的 PP3,更新 PTE3儡蔓,隨后返回」叮現(xiàn)在 VP3 已經(jīng)緩存在 DRAM 中了,地址翻譯硬件也可以繼續(xù)正常處理喂江。
分配頁(yè)面
當(dāng)未分配的頁(yè)面需要分配一個(gè)新的虛擬頁(yè)面時(shí)候召锈。如下圖所示,需要進(jìn)行 VP5 分配获询,就是在磁盤上創(chuàng)建空間并更新 PTE5涨岁,使它指向磁盤上這個(gè)新創(chuàng)建的頁(yè)面。
多級(jí)頁(yè)表
現(xiàn)代 64 位操作系統(tǒng)一般都采用 48 位的虛擬地址空間吉嚣,假設(shè)每個(gè)虛擬頁(yè)大小為 4KB梢薪,每個(gè) PTE 大小為 8byte,那么整個(gè)頁(yè)表的大小將是: 尝哆。這對(duì)任何系統(tǒng)來說都是難以承受的秉撇。
于是就引入了多級(jí)頁(yè)表的概念,多級(jí)頁(yè)表將 VPN (VPN,VPO 概念可見下面內(nèi)容)劃分為多個(gè)層級(jí)琐馆,地址查詢的時(shí)候规阀,需要從一級(jí)頁(yè)表逐層進(jìn)行查詢。只有一級(jí)頁(yè)表需要常駐內(nèi)存中瘦麸,虛擬內(nèi)存系統(tǒng)可以在需要的時(shí)候才調(diào)出次級(jí)頁(yè)表谁撼,這樣就減少了對(duì)主存的壓力。同時(shí)對(duì)一個(gè)普通應(yīng)用程序而言滋饲,在使用一個(gè) 48 位的虛擬地址空間時(shí)候厉碟,絕大部分虛擬地址空間應(yīng)該都是未分配的,如果一級(jí)頁(yè)面的地址空間是未分配的了赌,后面的次級(jí)頁(yè)表肯定也都是空的墨榄。
地址翻譯
地址翻譯的時(shí)候,CPU 中一個(gè)控制寄存器--頁(yè)表基址寄存器(Page Table Base Register, PTBR)指向當(dāng)前頁(yè)表勿她。虛擬地址被分為兩部分:
- 虛擬頁(yè)面偏移:Virtual Page Offset袄秩,VPO,位數(shù)為:
- 虛擬頁(yè)號(hào):Virtual Page Number逢并,VPN之剧,虛擬頁(yè)的唯一標(biāo)識(shí)
MMU 使用 VPN 找到對(duì)應(yīng)的 PTE,可以采取一些放置策略來加速查找的速度砍聊,例如讓 VPN 0 對(duì)應(yīng) PTE 0背稼,VPN 1 對(duì)應(yīng) PTE 1,以此類推玻蝌。如果有效位是 1 蟹肘,將從 PTE 中獲取的物理頁(yè)號(hào)(Physical Page Number,PPN)與虛擬地址中的 VPO 拼合起來就可以得到需要的物理地址(因?yàn)槲锢眄?yè)跟虛擬頁(yè)的大小是一樣的俯树,所以物理頁(yè)面偏移(Physical Page Offset帘腹,PPO)與 VPO 是一樣的)。查找過程如下圖所示许饿。
TLB
CPU 運(yùn)行是非逞粲快的,如果每次 CPU 產(chǎn)生虛擬地址的時(shí)候陋率,MMU 都需要從頁(yè)表中進(jìn)行查閱球化,最壞情況下會(huì)要求從內(nèi)存中多取一次數(shù)據(jù),代價(jià)是幾十到幾百個(gè)時(shí)鐘周期瓦糟。即使 PTE 被緩存在了高速緩存中筒愚,依然會(huì)有損耗。為了消除這樣的損耗菩浙,在 MMU 中加入了一個(gè) PTE 的小緩存锨能,被稱為翻譯后備緩沖器(Translation Lookaside Buffer扯再,TLB)。
TLB 是一個(gè)小的址遇,虛擬尋址的緩存,每行保存著一個(gè) PTE斋竞。為了快速在 TLB 中找到 PTE倔约,虛擬地址被劃分為如下形式,跟之前講解的緩存劃分一樣坝初,只不過由于 TLB 每行只有一條數(shù)據(jù)浸剩,所以塊偏移是 0 位。
VPN 被劃分成了如下兩部分:
- TLB索引:TLBI鳄袍,位數(shù)為绢要,用于快速定位緩存組
- TLB標(biāo)記:TLBT,位數(shù)為:VPN位數(shù)-TLBI位數(shù)拗小,在一個(gè)緩存組有多行時(shí)候重罪,查詢正確的 PTE
上圖展示了利用 TLB 進(jìn)行地址轉(zhuǎn)換時(shí)候的 TLB 命中跟不命中情況。
- CPU 產(chǎn)生一個(gè)虛擬地址
- MMU 利用 VPN 從 TLB 中獲取 PTE哀九,命中的話就直接返回 PTE剿配。否則就需要通過高速緩存/內(nèi)存獲取 PTE,并在 TLB 中進(jìn)行緩存
- MMU 將虛擬地址翻譯成為物理地址阅束,并將其發(fā)送至高速緩存/內(nèi)存
- 高速緩存/內(nèi)存將所請(qǐng)求的數(shù)據(jù)返回給 CPU
虛擬地址作為內(nèi)存包含工具
現(xiàn)代計(jì)算機(jī)必須為操作系統(tǒng)提供手段來控制內(nèi)存的訪問呼胚。
- 不應(yīng)該允許一個(gè)用戶進(jìn)程修改它的只讀代碼段
- 不允許它讀或者修改任何內(nèi)核中的代碼和數(shù)據(jù)
- 不允許它讀或者寫其他進(jìn)程的私有內(nèi)存
- 不允許它修改任何與其他進(jìn)程共享的虛擬頁(yè)面,除非所有共享者都顯式的允許
通過在 PTE 上添加一些額外的許可位息裸,來實(shí)現(xiàn)對(duì)一個(gè)虛擬頁(yè)面的訪問控制變的十分簡(jiǎn)單蝇更。如下圖所示,可以添加三個(gè)許可位呼盆, SUP 位表示必須允許在超級(jí)用戶模式下才能訪問該頁(yè)年扩,READ 和 WRITE 位控制對(duì)頁(yè)的讀、寫權(quán)限宿亡。
例如常遂,進(jìn)程 i 允許在用戶模式下的話,那么它有讀 VP0 和讀寫 VP1 的權(quán)限挽荠,但是不允許訪問 VP2克胳。
在多級(jí)頁(yè)表的情況下,父頁(yè)表跟底層頁(yè)表的標(biāo)識(shí)也是不同的圈匆,如下圖給出了一個(gè)實(shí)際系統(tǒng)中的頁(yè)表?xiàng)l目格式漠另。
實(shí)例分析
上圖是 Intel Core i7(Haswall)的 CPU 封裝笆搓,用于內(nèi)存訪問的 Core i7 的核心參數(shù)如下:
- L1 d-TLB 為 64 個(gè)條目性湿,4路,所以有 16 組
- 64 byte CacheLine满败,4KB PageSize
- L1 d-cache 為 32KB肤频,8路,所以有 64 組
- 支持 48 位虛擬地址算墨,52 位物理地址
- 4 級(jí)頁(yè)表
上圖是 Core i7 結(jié)合了虛擬地址宵荒、高速緩存的實(shí)際內(nèi)存訪問過程實(shí)例。
- 48 位虛擬地址被劃分為 12 位 VPO(PageSize 為 4KB)及 36 位 VPN
- 進(jìn)行虛擬地址翻譯净嘀,首先查找 TLB 中是否存在緩存
a. VPN 根據(jù) TLB 緩存情況被劃分為 4 位 TLBI(16組)及 32 位 TLBT报咳,首先使用 TLBI 定位組,再使用 TLBT 逐行匹配挖藏,圖中 1 行 4 塊其實(shí)是代表的 1 組 4 路
b. 如果命中 TLB 緩存暑刃,從獲取的 PTE 中取出 PPN 與 VPO 組成物理地址
c. 如果未命中 TLB 緩存,需要從頁(yè)表進(jìn)行查詢膜眠,36 位 VPN 被按照 9 位一組劃分成了 4 級(jí)頁(yè)表岩臣,查詢到 PTE 后,取出 PPN 與 VPO 組成物理地址柴底,同時(shí)也會(huì)緩存 PTE 到 TLB 中 - 獲取到物理地址后婿脸,首先查詢 L1 緩存中是否有緩存
a. 物理地址被劃分為 3 部分,6位 CO(CacheLine 為 64 byte)柄驻,6位 CI(L1 緩存為 64組)及 40 位 CT狐树。首先使用 CI 定位緩存組,再使用 CT 逐行匹配
b. 如果命中 L1 緩存鸿脓,取出緩存行后使用 CO 定位數(shù)據(jù)位置抑钟,獲取數(shù)據(jù)內(nèi)容
c. 如果未命中 L1 緩存,則按照既定規(guī)則野哭,查詢 L2在塔、L3、主存拨黔,直至獲取到數(shù)據(jù)內(nèi)容
Linux 虛擬內(nèi)存系統(tǒng)
這塊看了好幾遍其實(shí)還是比較迷糊蛔溃,先記錄下來吧,也希望有比較明白的同學(xué)能留言給指教篱蝇。
Linux 為每個(gè)進(jìn)程維護(hù)了一個(gè)單獨(dú)的虛擬地址空間贺待,如圖所示
內(nèi)核虛擬內(nèi)存包含內(nèi)核中的代碼和數(shù)據(jù)結(jié)構(gòu),內(nèi)核虛擬內(nèi)存的某些區(qū)域被映射到所有進(jìn)程共享的物理頁(yè)面(這樣就不需每個(gè)進(jìn)程將運(yùn)行所必須的內(nèi)核代碼都加載一遍)零截。Linux 也將一組連續(xù)的虛擬頁(yè)面(大小等于系統(tǒng) DRAM 的總量)映射到一組連續(xù)的物理頁(yè)面麸塞,這樣為內(nèi)核提供一種可以便利的方法來訪問物理內(nèi)存中的任何特定的位置。
內(nèi)核虛擬內(nèi)存的其他區(qū)域包含每個(gè)進(jìn)程都不相同的數(shù)據(jù)涧衙,比如哪工,頁(yè)表奥此、內(nèi)核在進(jìn)程的上下文執(zhí)行代碼是使用的棧,以及記錄虛擬地址空間當(dāng)前組織的各種數(shù)據(jù)結(jié)構(gòu)雁比。
深入理解計(jì)算機(jī)系統(tǒng)