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