【自制操做系統05】開啓內存分頁機制

經過前四章的努力,咱們成功將控制權轉交給了 loader.asm 這個程序,而且從實模式跨越到了保護模式。第四章講保護模式的時候我說過,這是咱們操做系統的第一個精彩之處。但其實這只是針對以前咱們進行的只是無心義的輸出,以及硬盤的加載等工做。但到了這一章,以前一步步的努力進入到了保護模式,也只能說是作了不少苦力,其實不少代碼都是固定的,給咱們發揮的空間也不大。html

可是到了本章,能夠說終於有能體現出咱們設計能力的地方了。git

1、實現分頁要作哪些事

仍是先直接簡單說要作的事,再說爲何,實現分頁要作如下三件事:數據結構

  1. 在內存某位置寫好頁表
  2. 頁目錄地址賦值給 cr3 寄存器
  3. 將 cr0 寄存器的 pg 位置 1

咱們對比下進入保護模式中實現段描述符機制須要作的三件事:oop

  1. 在內存某位置寫好段描述符表
  2. 段描述符表地址賦值給 gdtr 寄存器
  3. 將 cr0 寄存器的 pe 位置 1(這個實際上是開啓保護模式)

你看,是不是很是類似呢?都是內存某位置準備xxx,把起始地址賦值給一個特定的寄存器,而後將另外一個特殊寄存器的某位置 1 表示開啓。因此上一章我說過,cpu 與操做系體打配合,這種模式運用得很是多。咱們寫操做系統的人不用管 cpu 的具體實現,只須要按照指定步驟操做便可,以後硬件會幫咱們完成所須要的功能。學習

2、爲何要分頁

說實話我也想不明白爲何要分頁,主要是我說不上來爲何不是其餘方式,因此這塊我也只能跟着官方說的去理解了。操作系統

若是隻用段式管理的話,段大小不一致,且同一個程序邏輯地址和物理地址都是連續的。段大小不一致致使內存有大段有小段,也會留下一些內存碎片,過大的段查不進來,太小的段插進去又會產生更小的碎片。同一個段內全部的程序地址都是連續的,這也致使不靈活,咱們但願能有一套機制使得程序所用的邏輯地址連續,但實際映射到的物理地址並不連續,增長這麼一個層來解決這個問題。設計

咱們本講只是準備一些必要的頁表,而後開啓頁表機制。等到後面多任務的時候才能真正體會到頁表的用處以及好處,因此咱們姑且先簡單理解下,至於具體的好處,其實有好多細節的,等之後用到的時候慢慢體會。code

3、頁表長什麼樣以及虛擬地址到物理地址的轉換

咱們能夠類比段的轉化,咱們最初給的地址是 段選擇子:段內偏移值,在保護模式下,用段選擇子去內存中的段描述符表中,找到段描述符,取出段基址,再+段內偏移地址,獲得最終的物理地址。htm

頁的轉化也是相似的,上一步經過段描述符獲得的「物理地址」,再開啓分頁後叫作邏輯地址。這個邏輯地址也是分紅 前半部分:後半部分 這種形式,用前半部分的值在頁表中尋找並換出一個頁地址(也能夠理解成基址這個概念),而後再拼接上後半部分的值,獲得最終的物理地址。blog

只不過,如今的頁表方案通常是二級頁表,第一級叫頁目錄表(PDE),第二級叫頁表(PTE)。而後這個邏輯地址就是被當作 高10位:中間10位:後12位。高10位負責再頁目錄表中找到一個頁目錄項,這個頁目錄項的值加上中間10位拼接後的地址去頁表中去尋找一個頁表項,這個頁表項的值,再加上後12位,拼接後的地址就是最終的物理地址。

12位能夠表示 4K,因此也就是一個頁可表示的內存大小爲 4KB。10位能夠表示 1K,因此頁目錄表中最多有 1024 個頁目錄項,一個頁表中最多有 1024 個頁表項,那最大可表示的內存範圍就是 1024 * 1024 * 4KB = 4G。其實這也是廢話,你能夠仔細想一想看,不論你分紅幾級頁表,只要是經過這種方式尋址的,只要是一個 32 位的地址,老是能夠表示 4G 大小的。只不過經過你的不一樣分法,可能致使頁大小,頁目錄項數目,頁表數目,以及假如你定了 n 級頁表後的 n 級頁表的頁表項數目不一樣而已。

頁目錄表和頁表的數據結構

虛擬地址到物理地址的轉換

4、頁表設計

咱們這樣設計頁表:

  • 頁目錄表的第 0 項和第 768 項,都對應緊接着的第一個頁表,映射了低端 1M 的物理內存(0x00000-0x100000),也就是說邏輯地址的開端 1M 和 3G 以上的第一個 1M 地址,都對應這物理內存的地段 1M。
  • 頁目錄表的第 769~1022 項,分別日後對應 254 個頁表,不過這些頁表尚未寫,先空着
  • 頁目錄表的第 1023 項,其地址指向該頁目錄表自己(也就是把頁目錄表看成頁表去理解了),經過這種方式能夠訪問頁目錄表自己。(這塊其實我也沒理解爲啥要這麼搞,無非就是想用虛擬地址訪問到這個頁表自己嘛。

爲何這樣設計呢?

由於咱們分頁以前的代碼(loader)都在低端 1MB 範圍內,因此開啓分頁以後的邏輯地址開始的 1M 也要一一對應上物理地址的開始 1M,因此有了第 0 個頁目錄項。第 768 個頁目錄項對應着邏輯地址 3G 以上的 4M( 0xc0000000~0xc03fffff 不過咱們頁表只寫了 256 項也就是規劃了 1M),這是由於咱們決定將操做系統內核寫在 3G 以上的 1M 空間裏

咱們規劃,虛擬地址的 0~3G 是用戶空間,3~4G 是內核空間,因此咱們提早把頁目錄表的第 769~1022 項建好,至於爲何之後再說。

5、上代碼

loader.asm

...
;建立頁表並初始化(頁目錄和頁表)
PAGE_DIR_TABLE_POS equ 0x100000
call setup_page

;從新加載 gdt,由於已經變成了虛擬地址方式
sgdt [lgdt_value]
mov ebx,[lgdt_value+2]
or dword [ebx+0x18+4],0xc0000000
add dword [lgdt_value+2],0xc0000000
add esp,0xc0000000

;頁目錄表起始地址存入 cr3 寄存器
mov eax,PAGE_DIR_TABLE_POS
mov cr3,eax

;開啓分頁
mov eax,cr0
or eax,0x80000000
mov cr0,eax

;從新加載 gdt
lgdt [lgdt_value]

mov byte [gs:0x1e0],'p'
mov byte [gs:0x1e2],'a'
mov byte [gs:0x1e4],'g'
mov byte [gs:0x1e6],'e'
mov byte [gs:0x1ea],'o'
mov byte [gs:0x1ec],'n'

jmp $

setup_page:
;先把頁目錄佔用的空間逐字清零
    mov ecx,4096
    mov esi,0
.clear_page_dir:
    mov byte [PAGE_DIR_TABLE_POS+esi],0
    inc esi
    loop .clear_page_dir
    
;開始建立頁目錄項(PDE)
.create_pde:
    mov eax,PAGE_DIR_TABLE_POS
    add eax,0x1000; 此時eax爲第一個頁表的位置及屬性
    mov ebx,eax
    or eax,111b
    mov [PAGE_DIR_TABLE_POS],eax
    mov [PAGE_DIR_TABLE_POS+0xc00],eax
    sub eax,0x1000
    mov [PAGE_DIR_TABLE_POS+4*1023],eax

;開始建立頁表項(PTE)
    mov ecx,256
    mov esi,0
    mov edx,111b
.create_pte:
    mov [ebx+esi*4],edx
    add edx,4096
    inc esi
    loop .create_pte
    
;建立內核其餘頁表的頁目錄項(PDE)
    mov eax,PAGE_DIR_TABLE_POS
    add eax,0x2000
    or eax,111b
    mov ebx,PAGE_DIR_TABLE_POS
    mov ecx,254
    mov esi,769
.create_kernel_pde:
    mov [ebx+esi*4],eax
    inc esi
    add eax,0x1000
    loop .create_kernel_pde
    ret
...

6、運行

Makefile 仍然和上一章同樣,因此直接執行 make brun

能夠看到分頁開啓後,成功在屏幕輸出了 pageon 字符串

7、學到這的一些感悟

我以前寫過一篇文章 究竟什麼是技術,還被好多人罵了。我文章裏說的就是感受如今作的事情(Java),以及好多好多所謂的技術,都只是應用而已,甚至以爲只有基礎科學,只有研究質子中子電子,這些東西纔算是真正的技術,其餘的只是應用而已。

不過如今我知道本身的問題所在了,由於我研究操做系統就是想作點真正的技術。但如今看來,若是還延續當時的想法,像開啓分頁,進入保護模式,往顯卡映射的內存寫數據,這些都應該只叫作應用。由於這些的底層原理是 cpu 硬件電路的佈線方式,咱們的操做系統只是應用了它們,把一些操做封裝起來再暴露給用戶而已。

但若是真這樣深刻下去,實際上是沒完沒了的,你的求知慾又會深刻到物理層面,這其實跟計算機技術已經相差甚遠了。因此我如今以爲,把底層細節看成已知,在這上面創建一套完善的體系,這自己就是這一層的技術了,每一層有每一層技術的複雜性,不能說越底層的才越接近技術,越接近真理。

因此,你能夠不斷深刻探索底層的技術,但大可沒必要對本身所研究層次的知識妄自菲薄。

寫在最後:開源項目和課程規劃

若是你對自制一個操做系統感興趣,不妨跟隨這個系列課程看下去,甚至加入咱們,一塊兒來開發。

參考書籍

《操做系統真相還原》這本書真的贊!強烈推薦

項目開源

項目開源地址:https://gitee.com/sunym1993/flashos

當你看到該文章時,代碼可能已經比文章中的又多寫了一些部分了。你能夠經過提交記錄歷史來查看歷史的代碼,我會慢慢梳理提交歷史以及項目說明文檔,爭取給每一課都準備一個可執行的代碼。固然文章中的代碼也是全的,採用複製粘貼的方式也是徹底能夠的。

若是你有興趣加入這個自制操做系統的大軍,也能夠在留言區留下您的聯繫方式,或者在 gitee 私信我您的聯繫方式。

課程規劃

本課程打算出系列課程,我寫到哪以爲能夠寫成一篇文章了就寫出來分享給你們,最終會完成一個功能全面的操做系統,我以爲這是最好的學習操做系統的方式了。因此中間遇到的各類坎也會寫進去,若是你能持續跟進,跟着我一塊寫,必然會有很好的收貨。即便沒有,交個朋友也是好的哈哈。

目前的系列包括

相關文章
相關標籤/搜索