第2章 存儲管理
2.1 Linux內存管理的基本框架
2.2 地址映射的全過程
Linux會在不同的cpu上運行,相應的80386也不僅僅只會跑Linux系統,所以系統和cpu之間的配合並不是完美的。
2.2.1 邏輯地址到線性地址
邏輯地址到線性地址即段映射階段。假設整個系統的映射機制都已經建立好,cpu正在執行call 08048368去執行另一段代碼。
在段映射階段首先要根據段寄存器裏的值去選擇一個合適的段描述符。cpu根據所執行指令的不同去選擇不同的段寄存器,由於這裏的call指令是去執行別的代碼段的內容,所以采用CS寄存器裏的值作為段選擇符。
那麽此時此刻CS裏的值是多少?CS的值是在什麽時候被設置成多少,又會在什麽時候被修改? 內核在新建一個進程的時候需要將該進程所需要使用的段寄存器全部設置好,代碼如下:
411到414是用來設置ds es ss cs寄存器值的地方,後面的宏定義的常量是被設置成的值。可以看出兩個很有意思的地方:1、除了cs寄存器外,其余的幾個寄存器都是一樣的值 2、把宏常量設置成寄存器的值,所以所以進程的cs ds es ss內容是一樣的。Intel工程師分出來的代碼段、數據段、堆棧段到了Linux這裏只剩下了代碼段和數據段。
整個Linux內核裏就四個段選擇符,如下所示,並且從TI=0可以看出全都放在了GDT表裏了,而且在Linux裏基本上不使用LDT。
回到程序,現在段映射需要做的是去GDT表裏把index=4的段描述符拿出來,GDT表的定義和裏面的段描述符如下所示。
總的來說這四個段的基址都是0x0,長度都是0xffffff ,並且都在內存裏,也就是是說內核不會把整個段全部置換到硬盤上。他們的不同之處主要體現的訪問的權限(DPL)和該段存儲的是數據還是代碼。雖然看到現在LInux的段式映射徒有其表,但是在這個地方他還是完成了權限的檢查。比如段描述符要求的DPL為0,但是CS寄存器裏的DPL為3,那麽這次訪問會被禁止。但其實這個檢查在後面的頁映射還會再做一次。
內核在新建一個進程的時候,為其分配CS寄存器的值(雖然是固定的),CS寄存器的一部分用於定位段描述符,另一部分用於規定該進程的權限,即可訪問的存儲區域是屬於內核還是用戶。從這個角度來看進程是被內核安全的管理的,一個進程在創建的時候就規定好了其權限。
2.2.2 線性地址到物理地址
經過段式映射,一個虛擬地址被映射成線性地址,而且這個線性地址和虛擬地址一樣,因為基址都是0。但最起碼從法理的角度現在他是線性地址了,整個過程就是在欺騙80386。
Linux裏線性地址到物理地址的映射需要經過三個步驟,這一點是和80386不一樣,80386只有兩個步驟。這個是LInux虛擬的映射模型,其實和80386原理都是一樣的,只不過多了一個步驟。為了解決這個矛盾,在LInux編譯的時候回根據CPU的型號選擇相應的編譯版本,在80386上運行的時候回選擇32位地址的兩層映射。其實從這裏很好的看出來的虛擬模型和實際模型的區別,虛擬模型是3層的但是受限於硬件只能改成兩層的。
下面這個文件定義了如何去使用32位的線性地址,也就定義了如何從線性地址映射到物理地址。第一個PGDIR_SHFIT定義了PGD索引的起始位置,bit22到bit31一共10位,索引是10位的,所以一個PGD表裏可以有的PGD的個數是1024個。接下來的PMD_SHIFT也是22,從bit22到bit22就是一位也沒有,也就是說PMD只有一個,這樣以來三層映射到了386這裏變成了兩層,其實從結果來看就是跳過了PMD這一層,但在LInux源碼的角度來說是三層映射。
整個地址的映射是由MMU來管理的,所以雖然說是CPU來完成了整個地址轉換過程,但是其實本質上是MMU來完成的,在80836上LInux的頁式管理如下。
在一個進程上處理機的時候,內核會使用特權指令設置好PGD的BASE,存在寄存器CR3裏,這個PGD的地址是進程獨享的,也就是每一個進程的PGD的地址不一樣,這一點和段式映射都使用同一個GDT不一樣。然後MMU用線性地址的第一個位段即高10位在PGD裏找到相應的項,然後MMU用線性地址中的PT段作為下表在PGD表項裏找到相應的PTE,在PTE裏存的是就是物理地址的BASE,然後線性地址剩下的部分用於和物理地址的BASE組合得到真正的物理地址。
整個映射過程CPU要訪問三次內存:取PGD,取PT,取PTE,所以虛存的高效實現要依賴於緩存。
2.3 幾個重要的數據結構和函數
在整個邏輯地址到物理地址的映射過程中主要是由MMU和幾個寄存器的來完成,LInux需要做的是在一個進程上處理機的時候設置好相應的寄存器的值和在內存中存放合理的數據,比如設置gdtr的值和GDT,準備好PGD、PT。本節介紹的是這個LInux在管理內存映射的時候需要用幾個函數和數據結構。
在頁式映射階段使用的PGD PMD PT定義如下,我們當然使用上面的那部分結構體的定義。
頁式映射的最後一站是PTE到物理地址的映射,PTE的定義包含了兩個部分,32位的PTE指針高20位是作為指針來使用,低12位用來表示頁面的狀態信息和訪問權限,具體來說是9位用來表示權限和標誌位。這些權限標誌位表示該地址是否在內存中、其權限大小、是否可以使用緩存等等、
內核裏有一個全局變量指向一個page數組,每一個page代表一個物理頁面,那麽整個page數組就代表整個物理頁面。所以PTE的高20是這個數組的索引,拿著這個索引就可以在數組裏定位到想要的物理頁面。這個索引定位的過程是從軟件的角度來說的,從硬件的角度來說,MMU需要把20位的地址和剩下的訪問權限結合在一起然後去物理內存上定位並檢驗權限,事實上高20位補上12個零就是就是物理頁的起始地址。如果MMU的工作過程正常結束,那麽通過索引下標訪問page數組的過程就正常結束,反之就會拋出相應的異常。
MMU在進行在拿著高20位進行映射前會先檢查其權限,在檢查權限的過程中會首先檢查P標誌位,即_PAGE_PRESENT,該位為1的時候表示對應的物理頁在內存中,後面才會繼續。這是一種物理頁定位失敗的情況,除此之外還有另一種失敗的情況。在拿著32為的線性地址一步步定位PTE地址的時候,發現PTE整個內容為0,則說明該物理內存和虛擬內存的映射還沒有建立成功。
第2章 存儲管理