【自制操作系統05】開啟內存分頁機制


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

但是到了本章,可以說終於有能體現出我們設計能力的地方了。

一、實現分頁要做哪些事

還是先直接簡單說要做的事,再說為什么,實現分頁要做以下三件事:

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

我們對比下進入保護模式中實現段描述符機制需要做的三件事:

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

你看,是否是非常相似呢?都是內存某位置准備xxx,把起始地址賦值給一個特定的寄存器,然后將另一個特殊寄存器的某位置 1 表示開啟。所以上一章我說過,cpu 與操作系體打配合,這種模式運用得非常多。我們寫操作系統的人不用管 cpu 的具體實現,只需要按照指定步驟操作即可,之后硬件會幫我們完成所需要的功能。

二、為什么要分頁

說實話我也想不明白為什么要分頁,主要是我說不上來為什么不是其他方式,所以這塊我也只能跟着官方說的去理解了。

如果只用段式管理的話,段大小不一致,且同一個程序邏輯地址和物理地址都是連續的。段大小不一致導致內存有大段有小段,也會留下一些內存碎片,過大的段查不進來,過小的段插進去又會產生更小的碎片。同一個段內所有的程序地址都是連續的,這也導致不靈活,我們希望能有一套機制使得程序所用的邏輯地址連續,但實際映射到的物理地址並不連續,增加這么一個層來解決這個問題。

我們本講只是准備一些必要的頁表,然后開啟頁表機制。等到后面多任務的時候才能真正體會到頁表的用處以及好處,所以我們姑且先簡單理解下,至於具體的好處,其實有好多細節的,等以后用到的時候慢慢體會。

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

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

頁的轉化也是類似的,上一步通過段描述符得到的“物理地址”,再開啟分頁后叫做邏輯地址。這個邏輯地址也是分成 前半部分:后半部分 這種形式,用前半部分的值在頁表中尋找並換出一個頁地址(也可以理解成基址這個概念),然后再拼接上后半部分的值,得到最終的物理地址。

只不過,現在的頁表方案一般是二級頁表,第一級叫頁目錄表(PDE),第二級叫頁表(PTE)。然后這個邏輯地址就是被看成 高10位:中間10位:后12位。高10位負責再頁目錄表中找到一個頁目錄項,這個頁目錄項的值加上中間10位拼接后的地址去頁表中去尋找一個頁表項,這個頁表項的值,再加上后12位,拼接后的地址就是最終的物理地址。

12位可以表示 4K,所以也就是一個頁可表示的內存大小為 4KB。10位可以表示 1K,所以頁目錄表中最多有 1024 個頁目錄項,一個頁表中最多有 1024 個頁表項,那最大可表示的內存范圍就是 1024 * 1024 * 4KB = 4G。其實這也是廢話,你可以仔細想想看,不論你分成幾級頁表,只要是通過這種方式尋址的,只要是一個 32 位的地址,總是可以表示 4G 大小的。只不過通過你的不同分法,可能導致頁大小,頁目錄項數目,頁表數目,以及假如你定了 n 級頁表后的 n 級頁表的頁表項數目不同而已。

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

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

四、頁表設計

我們這樣設計頁表:

  • 頁目錄表的第 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 項建好,至於為什么以后再說。

五、上代碼

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
...

六、運行

Makefile 仍然和上一章一樣,所以直接執行 make brun

可以看到分頁開啟后,成功在屏幕輸出了 pageon 字符串

七、學到這的一些感悟

我之前寫過一篇文章 究竟什么是技術,還被好多人罵了。我文章里說的就是感覺現在做的事情(Java),以及好多好多所謂的技術,都只是應用而已,甚至覺得只有基礎科學,只有研究質子中子電子,這些東西才算是真正的技術,其他的只是應用而已。

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

但如果真這樣深入下去,其實是沒完沒了的,你的求知欲又會深入到物理層面,這其實跟計算機技術已經相差甚遠了。所以我現在覺得,把底層細節當作已知,在這上面建立一套完善的體系,這本身就是這一層的技術了,每一層有每一層技術的復雜性,不能說越底層的才越接近技術,越接近真理。

所以,你可以不斷深入探索底層的技術,但大可不必對自己所研究層次的知識妄自菲薄。

寫在最后:開源項目和課程規划

如果你對自制一個操作系統感興趣,不妨跟隨這個系列課程看下去,甚至加入我們(下方有公眾號和小助手微信),一起來開發。

參考書籍

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

項目開源

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

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

如果你有興趣加入這個自制操作系統的大軍,也可以在留言區留下您的聯系方式,或者在 gitee 私信我您的聯系方式。

課程規划

本課程打算出系列課程,我寫到哪覺得可以寫成一篇文章了就寫出來分享給大家,最終會完成一個功能全面的操作系統,我覺得這是最好的學習操作系統的方式了。所以中間遇到的各種坎也會寫進去,如果你能持續跟進,跟着我一塊寫,必然會有很好的收貨。即使沒有,交個朋友也是好的哈哈。

目前的系列包括


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM