ZeroOS—第2章—內存管理模塊(1)

IA32內存管理結構簡介

內存管理模塊是硬件相關的,所以在編寫內存管理模塊前,我們需要了解我們編寫內存管理代碼所基於的硬件信息——IA32內存管理機制,這裏涉及到的概念有虛擬地址、線性地址、物理地址、分段機制、分頁機制和段頁機制。這篇文章就對這些概念逐一進行講解,其中的內容基本來自《IA-32卷3:系統編程指南》(需要的留言),如果有不足或者錯誤,還請斧正。

虛擬、線性和物理地址

這三個地址在內存尋址的過程中的先後順序如下:虛擬地址—(分段機制)—>線性地址—(分頁機制)—>物理地址,其中線性地址到物理地址的轉化中,如果沒有開啓分頁機制,那麼線性地址就是物理地址,否則的話還要進過分頁機制的映射,而分段機制是不能被關閉的,虛擬地址必須經過分段機制的映射才能變爲線性地址。下面對三個地址的概念和它們之間的詳細關係進行逐一講解。

1.虛擬地址(邏輯地址)

虛擬地址是在代碼中使用的地址,在編譯生成可執行文件後,其中的每條指令、每個數據都有自己唯一的地址,這個地址就是虛擬地址。可執行文件,包括內核和用戶程序,在鏈接的時候都會有一個起始地址,內核的起始地址(入口地址)由內核的鏈接腳本給出,這個地址確定了內核第一條指令的虛擬地址,其他所有的指令和數據都會根據這個起始地址重新進行確定其自身的虛擬地址,這個過程被稱爲重定位。這個工作由鏈接器根據鏈接腳本來完成,在根據鏈接腳本確定了每個節的位置後,節中的標籤的位置也就確定了下來,此時鏈接器就會把所有引用了標籤的指令,比如jmp label或mov $0,(label)指令,將label標籤所對應的虛擬地址填入指令中,這樣可執行文件就可以正確的尋址了。

這裏要注意的是上述過程都是在程序的編譯連接階段完成的,此時程序都沒有加載進內存呢,所以這個地址是給代碼用的(或者說程序員)。對於IA32機器來說,虛擬地址是從0到4GB的,它是不管實際物理內存有多少的,哪怕實際物理內存只有4MB,它也能讓代碼在4GB的虛擬地址空間里正常運行,你就說牛批不牛批吧。那麼具體是怎麼實現的呢,這裏我將原理大概說一下:假如你有一列10米長的火車,需要在距離爲1000米的兩個地點來回跑,你說這個鐵路該怎麼鋪?那就在兩個地點鋪上1000米的鐵路就完了唄,但是不巧的是你只有兩節長度爲20米的鐵軌,哦吼。。。但是你還有一個效率奇高的施工隊,效率有多高呢,拆一段鐵路只用0.5秒,鋪一段鐵路也只用0.5秒,而且永不停工,永不喊累。不知道你想到沒有,反正因特爾的工程師早就想到了一個奇招,先鋪上40米讓火車跑,當火車跑到第二節鐵路時把第一節鐵路拆下來鋪在第二節鐵路的前面,這樣就能讓火車只用兩節20米的鐵軌在1000米的距離上自由通行。我剛理解這個思想的時候覺得簡直了,這是人乾的事兒???你這包工頭也太黑心了吧,啊,,,不是,你這想法也太巧妙了吧。

2.線性地址

如果將虛擬地址比作分段機制的原料,那麼線性地址就是分段機制的產品。在分段機制根據虛擬地址算出線性地址後,分段機制的任務就完成了,這個時候需要看處理器是否開啓了分頁,如果沒有開啓分頁,那麼線性就是下面所說的物理地址,若果開啓了分頁,則交給分頁機制進行處理。

線性地址是系統編程時需要了解的概念,編寫用戶程序是用不到這個概念。

3.物理地址

物理地址就是最後用來在物理內存中確定數據位置的,它會被放在地址總線上,用於尋址。

分段和分頁機制

在詳細講解分段和分頁機制前,我們先對這兩個機制的關係進行一個簡要的敘述,它們的關係同時也反映了在IA32架構下的內存尋址的過程。下面我們就下圖進行一一講解,如有不足,歡迎斧正。

1.分段機制

分段機制中最重要的是段描述符表(下文統稱段表),顧名思義其中包含的就是段描述符了,如圖是段描述符的定義:

屬性位(G),表示該段描述符的粒度,和段的界限結合後確定段的長度,爲0表示粒度爲1B,最大支持段長度爲1MB。爲1表示粒度爲4KB,最大支持段長度爲4GB。

屬性位(D/B),指定該段默認操作數的大小,爲0表示爲16位的段。爲1表示爲32位的段,在本課題內核中,該值爲1。

屬性位(AVL),這個位保留給操作系統使用。

屬性位(P),該位爲1表示該段存在,否則訪問該段會導致異常。

屬性位(DPL),描述符特權級,0最高,3最低。

屬性位(S),表示描述符的類型,爲0表示系統段,爲1表示代碼段或者數據段。

屬性位(TYPE),表示段的類型,依次表示是否可執行、是否位特權級依從、是否可讀、是否已訪問。

通過段描述符就可以確定一個內存段的起始地址和長度,當我們訪問內存時,必須確定是在哪個段描述符對應的段中進行訪存的,可是段表中有那麼多的段描述符,CPU是怎麼確定用哪個呢,這裏我們再引入一個概念——段選擇子,其定義如圖:

其中第3至15位確定段描述符在段表中的位置,第2位確定用全局段表(GDT)還是局部段表(LDT),這兩個的區別在哪呢,可以簡單地理解爲全局段表是可以給所有的進程使用的,而局部段表是給對應進程使用的,爲什麼說簡單理解呢,因爲目前ZOS不用LDT,內存管理的主要工作都在分頁機制上,學習分段機制的目的在於儘可能的繞過分段機制。對於請求特權級會在後面說,因爲還要介紹當前特權級和描述符特權級。

有了段表、段描述符和段選擇子,我們就可以詳細瞭解分段機制的運行過程了,首先需要存儲這三個信息,段表的起始地址存儲在GDT寄存器中,通過LGDT加載一個6字節內存區域至GDT寄存器,其中包括GDT的基地址和大小(以字節計),段選擇子存儲在段寄存器中(比如DS、CS),通過MOV指令即可載入段寄存器。

下面我們通過一個訪問內存數據的例子(AT&T風格):movl 0x100000,%eax,這條指令將內存0x100000處的數據複製進寄存器eax,其中分段機制運行如下:CPU從ds(默認的數據段寄存器)中找到數據段描述符,檢查權限(詳細見特權級小節),成功後檢查要訪問的地址是否在數據段描述符的範圍中,成功後將數據段的基地址(GRUB加載內核後這個值爲0)加上要訪問的地址得到線性地址:0x100000。分段機制的任務就完成了,接下來講解分頁機制的工作過程(前提是開啓了分頁)。

2.分頁機制

分頁機制是選用的,默認是關閉的,GRUB在加載完內核後也是默認關閉分頁機制的,所以我們要先打開分頁。這個開關叫分頁標誌:CRO寄存器的第31位是分頁機制的標誌位(PG),置1打開分頁硬件,這裏需要注意的是一旦PG被置位,處理器馬上就會進入分頁模式,所以在打開這個標誌前,應該將分頁機制所需的數據全被準備好。

還有一個標誌位是爲了使用4MB的超級頁,與它相對的是4KB的普通頁(暫且這麼叫吧)。CR4寄存器的第4位是分頁機制的頁尺寸擴展標誌位(PSE),置1允許使用4MB的超級頁,否則只使用4KB的頁。

分頁機制採用了二級頁表,通俗來講就是說線性地址映射到物理地址需要經過兩個頁表的映射。

分頁機制中首先要了解頁目錄(第一級),它是一個4KB連續的物理內存,其中存儲着1024個頁目錄項,每個頁目錄項代表大小爲4MB的物理內存,但是要注意的是每個頁目錄項所表示的4MB的物理內存不一定存在也不一定連續。其數據結構如圖:

對照英文也可以看明白大概意思,每一位的詳解如下:

第31至12位確定了一個4MB頁的首地址,所以地址必須是4MB對齊的,但實際上是指向了一個4KB的頁表頁,而不是一個4MB連續的物理頁。

第11至9位保留給操作系統使用。

第8位是全局標誌,若該標誌和CR4中的啓用全局頁標誌都置1,則當CR3寄存器被裝載或發生任務切換時,該頁在TLB中的緩存不會失效。

第7位確定頁的尺寸,該標誌只用於頁目錄表項,爲0時頁尺寸爲4KB,爲1時頁尺寸爲4MB。

第5位爲訪問標誌,由硬件置位,但是需要由軟件清零。

第4位是頁級高速緩存禁用標誌(PCD),爲1時該頁的高速緩存將會被禁用,即每次訪問都不會訪問TLB,而是直接訪問內存,當在頁表中映射一些映射在內存的硬件時,應該將此位置1。

第3位是寫直達標誌(PWT),爲1時啓用頁表的直寫高速緩存機制。

第2位是用戶/管理員標誌(U/S),爲0時只有CPL爲0的代碼可以訪問(就是內核代碼)能訪問該頁。

第1位是頁存在標誌(P),爲1表示對應的頁存在,如果訪問P=0的頁,則會導致缺頁中斷。

頁目錄項指向一個頁表(第二級),頁表也是一塊4KB連續的物理內存,其中存儲的是頁表項,頁表項指向最後的物理頁(頁框),它的結構如圖:

其中每一位意義的詳細解釋如下:

第31至12位爲物理地址的高20位。

第7位是頁屬性表索引(PAT)。

第6位爲髒頁標誌(D),由處理器置位,由操作系統清零。其餘位與頁目錄項中的意義相同。

頁表項所指的物理頁也是一塊4KB連續的物理內存,其中存儲的就是我們寫入的數據,比如指令和相關數據什麼的。

看到這裏有沒有看出來這三者的關係?是不是有一點像C語言中的數組(物理頁)、數組指針(頁表項)和二級數組指針(頁目錄項)。只不過指針中存儲的全部是地址信息,而頁表項和頁目錄項中還存儲了相關屬性。通過這個關係我們可以看出,這三者中只需要存儲頁目錄就可以了,這就是CR3寄存器——頁目錄地址寄存器,這個寄存器保存了頁目錄的物理地址,該地址必須是4KB對齊的。

有了上述概念後就可以詳細解釋分頁機制的工作過程了。其中映射4KB頁的工作過程:

對於使用4KB頁映射的線性地址來說,該線性地址被分爲三個部分,根據高十位的頁目錄索引找到該線性地址對應的頁目錄,這樣就找到了線性地址對應的頁表頁,然後根據中間十位的頁表索引找到對應的頁表項,這樣就找到線性地址對應的頁框,最後將頁框的起始地址和線性地址低12位的頁內偏移相加,就得到了最後的物理地址。

映射4MB頁的工作過程:

對於使用4MB頁的線性地址,只需要根據線性地址高十位的頁目錄索引找到對應的4MB頁,然後將超級頁的起始地址和線性地址的低二十二位相加,就得到了最後的物理地址。

IA32中的特權級詳解

當前特權級(CPL)

CPL 是當前執行程序或任務的特權級.它存在 CS 段寄存器和 SS 段寄存器的第 0 位和第 1 位中。一般地,CPL 與當前指令所在代碼段的特權級相等。當進程的控制流程轉到一個不同特權級的代碼段時,處理器就改變 CPL。當訪問一致性代碼段時,對 CPL 的處理會略有不同。一致性代碼段可以被任何數值上等於或者大於 (特權較低) 本一致性代碼段 DPL 的代碼訪問。同樣, 當處理器訪問一個與 CPL 特權級不一樣的一致性代碼段時, 不改變 CPL。

請求特權級(RPL)

RPL 是賦給段選擇子的取代性特權級,存儲在段選擇子的第 0 位和第 1 位。 處理器檢驗 RPL 的同時也檢驗 CPL 來決定對段的訪問是否被允許。即使請求訪問的進程或任務有足夠的特權(也就是說 CPL 權限夠了——譯註)去訪問一個段,但是,如果 RPL(即指向將要去訪問的這個段的段描述符的段選擇子中的 RPL——譯註)的特權級不夠,訪問會被拒絕。也就是說,如果這個段選擇子的 RPL 在數值上大於 CPL,RPL 就取代 CPL,反之亦然。RPL可用來確保特權代碼不代表應用程序去訪問的一個段, 除非這個應用程序本身有這個段的訪問特權。

描述符特權級(DPL)

DPL 是段或門的特權級。它存儲在段或門的描述符的DPL 域中。一旦當前執行的代碼段試圖訪問一個段或門時,處理器會將那個段或門的 DPL 與 CPL 以及那個段或門的選擇子的 RPL(本節後面進行描述)進行比較。根據所訪問的段或門的類型,對 DPL 的解釋也會不一樣:

1.數據段。DPL 指明訪問這個段的進程或任務的特權級可以具有的最大數值。比如,如果某個數據段的 DPL 是 1,只有運行在 CPL 爲 0 或 1 的進程纔可以訪問它。

2.非一致性代碼段(不使用調用門) 。DPL 指明訪問這個段的進程或任務應該具有的特權級。比如,某個非一致性代碼段的 DPL 爲 0,那麼只有 CPL爲 0 的進程才能訪問它。

3.調用門。 DPL 指明能夠訪問這個調用門的當前進程或者任務的特權級應該具有的最大數值。 (這個訪問規則同樣適用數據段。 )通過調用門訪問的一致性代碼段和非一致性代碼段。DPL 指明能訪問這個段的進程或任務的特權級的最低數值。比如,一個一致性代碼段的 DPL爲 2,那麼 CPL 爲 0 或 1 的進程就不能訪問它。

4.TSS。DPL 指明能夠訪問這個 TSS 的當前進程或者任務應該具有的特權級的最高數值。 (這個訪問規則同樣適用數據段。 

個人理解

上述都是IA32開發手冊上的內容,對於我自己而言,我是這樣理解特權級的:DPL只與CPL和RPL中數值最大(即特權級最低)的那個進行比較,如果DPL大於等於其中的最大值,那麼特權級檢查通過,否則禁止訪問併產生一個#GP異常。對於一致性和非一致性,非一致性代碼段的就是爲了防止低特權級訪問高特權級的數據,而一致性代碼段則是爲了防止高特權級訪問低特權級數據。

有了這些基本概念應該就夠了,下一篇我們就用這些概念再加一個簡單的內存管理算法對內存管理模塊進行設計。

發佈了15 篇原創文章 · 獲贊 8 · 訪問量 5621
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章