經過前四章的努力,咱們成功將控制權轉交給了 loader.asm 這個程序,而且從實模式跨越到了保護模式。第四章講保護模式的時候我說過,這是咱們操做系統的第一個精彩之處。但其實這只是針對以前咱們進行的只是無心義的輸出,以及硬盤的加載等工做。但到了這一章,以前一步步的努力進入到了保護模式,也只能說是作了不少苦力,其實不少代碼都是固定的,給咱們發揮的空間也不大。html
可是到了本章,能夠說終於有能體現出咱們設計能力的地方了。git
仍是先直接簡單說要作的事,再說爲何,實現分頁要作如下三件事:數據結構
咱們對比下進入保護模式中實現段描述符機制須要作的三件事:oop
你看,是不是很是類似呢?都是內存某位置準備xxx,把起始地址賦值給一個特定的寄存器,而後將另外一個特殊寄存器的某位置 1 表示開啓。因此上一章我說過,cpu 與操做系體打配合,這種模式運用得很是多。咱們寫操做系統的人不用管 cpu 的具體實現,只須要按照指定步驟操做便可,以後硬件會幫咱們完成所須要的功能。學習
說實話我也想不明白爲何要分頁,主要是我說不上來爲何不是其餘方式,因此這塊我也只能跟着官方說的去理解了。操作系統
若是隻用段式管理的話,段大小不一致,且同一個程序邏輯地址和物理地址都是連續的。段大小不一致致使內存有大段有小段,也會留下一些內存碎片,過大的段查不進來,太小的段插進去又會產生更小的碎片。同一個段內全部的程序地址都是連續的,這也致使不靈活,咱們但願能有一套機制使得程序所用的邏輯地址連續,但實際映射到的物理地址並不連續,增長這麼一個層來解決這個問題。設計
咱們本講只是準備一些必要的頁表,而後開啓頁表機制。等到後面多任務的時候才能真正體會到頁表的用處以及好處,因此咱們姑且先簡單理解下,至於具體的好處,其實有好多細節的,等之後用到的時候慢慢體會。code
咱們能夠類比段的轉化,咱們最初給的地址是 段選擇子:段內偏移值,在保護模式下,用段選擇子去內存中的段描述符表中,找到段描述符,取出段基址,再+段內偏移地址,獲得最終的物理地址。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 級頁表的頁表項數目不一樣而已。
由於咱們分頁以前的代碼(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 硬件電路的佈線方式,咱們的操做系統只是應用了它們,把一些操做封裝起來再暴露給用戶而已。
但若是真這樣深刻下去,實際上是沒完沒了的,你的求知慾又會深刻到物理層面,這其實跟計算機技術已經相差甚遠了。因此我如今以爲,把底層細節看成已知,在這上面創建一套完善的體系,這自己就是這一層的技術了,每一層有每一層技術的複雜性,不能說越底層的才越接近技術,越接近真理。
因此,你能夠不斷深刻探索底層的技術,但大可沒必要對本身所研究層次的知識妄自菲薄。
若是你對自制一個操做系統感興趣,不妨跟隨這個系列課程看下去,甚至加入咱們,一塊兒來開發。
《操做系統真相還原》這本書真的贊!強烈推薦
當你看到該文章時,代碼可能已經比文章中的又多寫了一些部分了。你能夠經過提交記錄歷史來查看歷史的代碼,我會慢慢梳理提交歷史以及項目說明文檔,爭取給每一課都準備一個可執行的代碼。固然文章中的代碼也是全的,採用複製粘貼的方式也是徹底能夠的。
若是你有興趣加入這個自制操做系統的大軍,也能夠在留言區留下您的聯繫方式,或者在 gitee 私信我您的聯繫方式。
本課程打算出系列課程,我寫到哪以爲能夠寫成一篇文章了就寫出來分享給你們,最終會完成一個功能全面的操做系統,我以爲這是最好的學習操做系統的方式了。因此中間遇到的各類坎也會寫進去,若是你能持續跟進,跟着我一塊寫,必然會有很好的收貨。即便沒有,交個朋友也是好的哈哈。
目前的系列包括