幾乎每一本將操作系統(tǒng)原理的書(shū)籍都會(huì)談及內(nèi)存管理方法的段頁(yè)式存儲(chǔ)。以前看書(shū)都是看的云里霧里!原因就是還沒(méi)有懂INTEL指令,不知道頁(yè)式存儲(chǔ)有什么作用。國(guó)內(nèi)教材有個(gè)最大的弊病就是,作者通常先將概念,然后再將如何運(yùn)用,很少去提及概念是如何被提出來(lái)!為什么非要提出這樣一個(gè)概念。
除非你已經(jīng)被一個(gè)問(wèn)題困擾到茶飯不思、輾轉(zhuǎn)難眠,否則你絕對(duì)不會(huì)明白一個(gè)新的概念對(duì)你有多大的作用
分段還是分頁(yè)
分段還是分頁(yè)其實(shí)不是兩個(gè)不同的概念!分段,其中心思想是將程序分成若干的邏輯段,每個(gè)段實(shí)現(xiàn)什么功能,提供什么資源。這個(gè)其實(shí)和后來(lái)面向?qū)ο蟮念惖乃枷霙](méi)有任何區(qū)別,僅僅是表現(xiàn)不一樣罷了。在過(guò)去編程時(shí),將程序分為若干邏輯段(數(shù)據(jù)段,32位代碼段,16位代碼段等),然后通過(guò)設(shè)置CS來(lái)實(shí)現(xiàn)段之間的跳轉(zhuǎn)。這種方式使得程序在設(shè)計(jì)期間邏輯清晰。但是這種方式,程序是一個(gè)線性的指令集合,某些時(shí)候,人們需要同時(shí)執(zhí)行兩個(gè)任務(wù),采用分段需要頻繁的切換CS,這樣造成很大的性能損耗。INTEL為了滿足這種能夠新增加一組寄存器 CR0、1、2、3來(lái)實(shí)現(xiàn)任務(wù)跳轉(zhuǎn)。這個(gè)使用同樣一個(gè)邏輯地址就能定位為不同的物理地址,如下圖
對(duì)于同樣一個(gè)邏輯地址10000,可以使用不同的分頁(yè)轉(zhuǎn)換,將邏輯地址轉(zhuǎn)化為不同的物理地址!
注:分段和分頁(yè)并不是對(duì)立的兩個(gè)概念,分頁(yè)其實(shí)建立在分段的基礎(chǔ)之上!
如何分頁(yè)
請(qǐng)思考如下一個(gè)數(shù)學(xué)題
y=f(x) {x的定義域?yàn)閇0,π/2]} ,找到一個(gè)函數(shù)使得f(x)的值域?yàn)閇0,1]?
如果你學(xué)過(guò)三角函數(shù)馬上就會(huì)想到 f(x)=sin(x)
用在分頁(yè)機(jī)制里,該數(shù)學(xué)題變?yōu)榱?/p>
y=f(x) {x的定義域?yàn)閇0,4.4M] 用4.4M的虛擬地址范圍 },找到一個(gè)函數(shù)使得f(x)的值域?yàn)閇0,4G]---32位CPU內(nèi)存尋址范圍?
解析:
這里當(dāng)然可以使用一個(gè)函數(shù)來(lái)實(shí)現(xiàn)這種擴(kuò)展,但是在計(jì)算機(jī)里面,沒(méi)有使用函數(shù),而是使用索引的方式來(lái)表示一個(gè)更大的范圍,所謂索引,就是新華字典里面的查詢頁(yè)!一本字典那么厚,但是可以使用幾頁(yè)查詢頁(yè)就能找到字典里面的任何一個(gè)字!
在INTEL x86CPU保護(hù)模式里,一頁(yè)通常值為4K(4K=2的12次方,需要12位來(lái)表示頁(yè)內(nèi)偏移地址),所以4G就是1M(1M=2的20次方)個(gè)4K,1M個(gè)頁(yè)索引。這樣一個(gè)物理地址就可以轉(zhuǎn)化為
20位頁(yè)索引+12位頁(yè)內(nèi):需要用1M個(gè)索引項(xiàng)去記錄每一頁(yè)的屬性!INTEL為了減小1M這個(gè)值,又將1M索引再次來(lái)個(gè)索引,將1M個(gè)索引按照1K個(gè)索引為1段,總共就是1K段!這樣一個(gè)屋里地址就轉(zhuǎn)化為:
10位頁(yè)索引的索引+10位頁(yè)索引+12位頁(yè)內(nèi)偏移地址,使用兩級(jí)索引來(lái)表示一個(gè)地址,地址轉(zhuǎn)換過(guò)程如下:
10位頁(yè)索引的所有集合(1M個(gè)索引)有個(gè)專門(mén)的名字:頁(yè)表,表中的每一項(xiàng)也就是每一個(gè)索引叫做頁(yè)表項(xiàng)PTE(page table entry)
10位頁(yè)索引的索引的所有集合(1K個(gè))有個(gè)專門(mén)的名字:頁(yè)目錄表,表中的每一項(xiàng)也就是每一個(gè)索引的索引叫做頁(yè)目錄表項(xiàng)PDE(page directory entry)
有一點(diǎn)值得強(qiáng)調(diào),頁(yè)表項(xiàng)和頁(yè)目錄表項(xiàng)不僅僅只有索引,只是上面為了理解簡(jiǎn)單而省略一些信息,他們都有各種的屬性!
每個(gè)項(xiàng)需要4個(gè)字節(jié),總共就需要 4*1K(1K個(gè)頁(yè)目錄表項(xiàng))+4*(1M個(gè)頁(yè)表項(xiàng)) = 4.4M!
實(shí)際上啟動(dòng)分頁(yè)的過(guò)程,也就是將在內(nèi)存中初始化f(x)的一個(gè)過(guò)程!具體而言就是在內(nèi)存中為每一個(gè)物理頁(yè)做索引,加屬性的過(guò)程!
當(dāng)這些信息都被正確的在內(nèi)存中初始化完畢之后,最重要的一步,就可以將信息的首地址復(fù)制給CR3,然后將CR0最高位設(shè)置為1就表示啟動(dòng)了分頁(yè)機(jī)制!
分頁(yè)還是不分頁(yè),分段還是不分段,保護(hù)模式還是實(shí)模式追索到源頭其實(shí)就是對(duì)CPU指令集的一個(gè)應(yīng)用,CPU提供了某些功能,然后由此編寫(xiě)的操作系統(tǒng)就啟用這些功能!所以不是操作系統(tǒng)多么牛逼,而是處理器多么厲害
分頁(yè)有多厲害
詳細(xì)解讀一下這句話:將信息的首地址復(fù)制給CR3。
CPU在將一個(gè)給定的虛擬地址進(jìn)行地址轉(zhuǎn)換的時(shí)候,首先是尋找CR3給定的頁(yè)目錄地址,找到之后,然后按照那個(gè)目錄地址開(kāi)始一級(jí)級(jí)的轉(zhuǎn)換,這里你可能就會(huì)問(wèn),那是不是我隨便給定一個(gè)CR3地址,那不是同一個(gè)地址會(huì)被轉(zhuǎn)換為隨便一個(gè)位置哦?是啊,就有這么牛逼!不同的頁(yè)目錄,就有不同的地址,操作系統(tǒng)實(shí)現(xiàn)的多任務(wù),虛擬地址空間就是通過(guò)設(shè)置不同的CR3來(lái)實(shí)現(xiàn)!現(xiàn)在是否能夠明白windows操作系統(tǒng)的每個(gè)進(jìn)程尋址空間呢?在虛擬地址框架下,不管你如何尋址,你肯定找不到別人進(jìn)程的代碼,除非你改變CR3的值!
此時(shí)你看看下面這張圖:
是不是覺(jué)得淺顯易懂了呢?
最后附上一段代碼,摘抄自《一個(gè)操作系統(tǒng)的實(shí)現(xiàn)》
; 啟動(dòng)分頁(yè)機(jī)制 --------------------------------------------------------------SetupPaging: ; 為簡(jiǎn)化處理, 所有線性地址對(duì)應(yīng)相等的物理地址. ; 首先初始化頁(yè)目錄 mov ax, SelectorPageDir ; 此段首地址為 PageDirBase mov es, ax mov ecx, 1024 ; 共 1K 個(gè)表項(xiàng) xor edi, edi xor eax, eax mov eax, PageTblBase | PG_P | PG_USU | PG_RWW.1: stosd add eax, 4096 ; 為了簡(jiǎn)化, 所有頁(yè)表在內(nèi)存中是連續(xù)的. loop .1 ; 再初始化所有頁(yè)表 (1K 個(gè), 4M 內(nèi)存空間) mov ax, SelectorPageTbl ; 此段首地址為 PageTblBase mov es, ax mov ecx, 1024 * 1024 ; 共 1M 個(gè)頁(yè)表項(xiàng), 也即有 1M 個(gè)頁(yè) xor edi, edi xor eax, eax mov eax, PG_P | PG_USU | PG_RWW.2: stosd add eax, 4096 ; 每一頁(yè)指向 4K 的空間 loop .2 mov eax, PageDirBase mov cr3, eax mov eax, cr0 or eax, 80000000h mov cr0, eax jmp short .3.3: nop ret; 分頁(yè)機(jī)制啟動(dòng)完畢 ----------------------------------------------------------
新聞熱點(diǎn)
疑難解答
圖片精選