一步步编写操作系统 51 加载内核4
咱們的內容都是連栽的,如果您沒看過我之前的文章,本節您是看不懂的。
接上節。
介紹完內核初始化的函數kernel_init后,本節代碼部分還差一點點沒說啦,下面看代碼:
…略 179 ;在開啟分頁后,用gdt新的地址重新加載 180 lgdt [gdt_ptr] ; 重新加載 181 182 ;;;;;;;;;;;;;;;;;;;;;;;;;;;; 此時不刷新流水線也沒問題 ;;;;;;;;;;;;;;;;;;;;;;;; 183 ;由于一直處在32位下,原則上不需要強制刷新,;經過實際測試沒有以下這兩句也沒問題. 184 ;但以防萬一,還是加上啦,免得將來出來莫句奇妙的問題. 185 jmp SELECTOR_CODE:enter_kernel ;強制刷新流水線,更新gdt 186 enter_kernel: 187 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 188 call kernel_init 189 mov esp, 0xc009f000 190 jmp KERNEL_ENTRY_POINT ; 用地址0x1500訪問測試,結果ok …略在代碼的開頭是咱們之前已經完成的重新加載GDT,就是將原來GDT的基地址0x900變成0xc0000900后重新加載。按理說,當前已經是32位環境啦,而且內核也是32位程序,不需要“顯式”地清空流水線。實話實說,之前loader.S中是沒有第185~186行,而且經我簡單測試后其運行結果也是正確的。不過,在調試過程中有可能會碰到稀奇古怪的問題,當然這絕對是人為的錯誤,不要輕易懷疑計算機。對于內核中這類“靈異”事件,咱們當然希望少碰到,某些bug真的會讓人調試好多天,所以為了保險起見,還是用無條件跳轉指令刷新了流水線,請大家知曉。
在進入內核之后,我們用的棧也要重新規劃了,棧起始地址不能再用之前的0xc0000900啦。為了方便編寫程序,我們在進入內核前將棧指針改成我們期待的值,在第189行,我們將esp改成了0xc009f000。此地址的選擇也是根據之前的內存布局圖。也許有同學會說,為什么不把esp選為0x9fc00,這才是最合理的。沒錯,您說的對,我們都是會過日子的人,0x9fc00確實是最省空間的選擇,這樣做,以后的程序也不會出錯。但這牽扯到以后要說的pcb,即程序控制塊(咱們在以后線程相關章節會細說pcb,這里僅要求大家對此有個淺表的了解即可),每個pcb都是自然頁,也就是要求4KB對齊,即4KB的范圍是0x000~0xfff,而不是類似0x333~0x1332這樣的范圍。我們打算在4KB內的最高地址做為棧底,如果以0x9fc00做為棧底,雖然不出會什么問題,但它顯得太個性了,比其它pcb少了0x400字節。所以,為了統一pcb大小,我們這里選擇棧底的要求是:它接近最大可用地址0x9fbff,并且以4KB對齊,所以0x9f000是最合適的。
為了打消部分同學的疑慮,容我再多說兩句。我擔心有同學可能會這樣想,咱們loader加載的物理地址是0x900,loader中使用的棧的棧底是0x900,棧是往下發展的,在loader以后的壓棧操作中,并不會破壞掉loader自身,似乎這種“完美”的方案可以在咱們的kernel中延續,也就是為何不讓kernel的棧為入口地址之下?比如咱們這里的入口地址是0xc0001500,也讓棧底esp為該值有何不妥。不管怎么說,如果有這種想法,說明您是個愛動腦的同學,我會為您悄悄點贊。其實loader.bin是純二進制文件,而kernel是elf格式的二進制文件,這兩者的區別是elf比純二進制文件多了文件頭,純二進制文件相當于elf文件中的所有段(segment)的集合。在前面我們分析過啦,程序的入口地址是很可能會在段中的,并不是在段的起始,就拿咱們的kernel.bin來說,它代碼段的入口地址是0xc0001500,起始地址卻是0xc0001000,入口并不在段的開頭。其實0xc0001000~0xc0001500之間的部分是文件頭,并不是真正的代碼。真要把esp賦值為0xc0001500,如您所料,將來內核中有壓棧操作時一定會破壞0xc0001000~0xc0001500之間的部分,雖然它只是文件頭不是實際代碼,似乎破壞了也無關緊要,但這樣做確實不美啊。您看,咱們的內核預計70KB左右,起始物理地址是0x1500,而棧底若為0x9f000,這0x9f000-0x1500約為630KB的空間,在正常情況下,棧是不會碰撞到內核的,這樣多省心。所以,兄弟們,我看還是算了吧,咱就選個高地址0x9f000,就它了。
啰嗦過后,loader通過第190行的跳轉指令進入內核。這里所見的KERNEL_ENTRY_POINT是boot/include/boot.inc中定義的宏,其值為0xc0001500,它正是我們用ld命令鏈接kernel時指定的代碼段地址,這個宏必須要與其一致才行。
經過這樣一番的規劃后,現在0x500~0x9fbff可用內存中,咱們自己的文件布局如圖:
和內核相關的內容咱們暫告一段落,在本章的結束,咱們說說保護模式下最閃亮的內容——特權
總結
以上是生活随笔為你收集整理的一步步编写操作系统 51 加载内核4的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何理财?理财的几点建议
- 下一篇: ffmpeg添加到环境变量_Window