自己動手從零寫桌面作業系統GrapeOS系列教程——13.向MBR中寫入程式

成宇佳發表於2023-03-14

學習作業系統原理最好的方法是自己寫一個簡單的作業系統。


前面鋪墊了這麼久,今天終於開始寫程式了。本講將介紹3個逐步深入但非常簡單的程式,一方面是讓大家熟悉開發流程,另一方面是順便解決前面遇到的CPU佔用率高的問題。

一、mbr1.asm回顧

mbr1.asm的程式碼之前我們介紹過,這裡我們回顧一下程式碼和演示步驟。
mbr1.asm程式碼如下:

;生成一個空的MBR
times 510 db 0 ;前510個位元組全為0
db 0x55,0xaa   ;最後兩個位元組是0x55和0xaa。

下面我們來演示:

1.啟動並登入CentOS

在VirtualBox中啟動CentOS虛擬機器,並用PowerShell登入到CentOS虛擬機器。

2.建立空虛擬硬碟

如果沒有虛擬硬碟或想重新建立一個空的虛擬硬碟,執行下面這條語句:

dd if=/dev/zero of=/media/VMShare/GrapeOS.img bs=1M count=4

截圖如下:

在上面截圖中,用hexdump命令檢視生成的虛擬硬碟檔案GrapeOS.img,可以看到每個位元組都是0,符合預期。

3.彙編mbr1.asm

nasm mbr1.asm -o mbr1.bin

截圖如下:

在上面截圖中,用hexdump命令檢視生成的mbr1.bin,共512個位元組,前510個位元組都是0,最後兩個位元組是0x55和0xaa,符合預期。

4.將mbr1.bin寫入到虛擬硬碟中

dd conv=notrunc if=mbr1.bin of=/media/VMShare/GrapeOS.img

截圖如下:

在上面截圖中,我們同樣用hexdump命令驗證,看到的確是將mbr.bin寫入到虛擬硬碟的第一個扇區中了。

5.啟動QEMU

在Windows的cmd命令列中執行如下命令:

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img

從截圖上可以看到,執行結果和之前的一樣。回顧到此為止,下面來解決CPU佔用率高的問題。

二、CPU佔用率高的原因

前面我們介紹過,在QEMU+GDB除錯中,反編譯16位程式碼是有問題的,下面來介紹另一種反編譯方法。nasm彙編器自帶了一個反彙編工具叫ndisasm,下面我們來反彙編mbr1.bin。

ndisasm mbr1.bin

截圖如下:

從截圖中可以看到兩個0位元組正好是一條彙編指令“add [bx+si],al”,最後的2個位元組0x55和0xaa也正好是指令“push bp”和“stosb”。但這些都不是我們要寫的程式,只是這些二進位制數正好是某條機器指令。從BIOS跳轉到0x7c00地址後,無論此處是什麼樣的二進位制數,CPU都會把它當作指令一條一條的執行。當執行完這512位元組,會繼續執行後面記憶體中的資料。而後面記憶體中的資料是不確定的,CPU就會亂執行半天,而且也沒有意義,這就是程式跑飛了。下面我們先來解決程式跑飛的問題。

三、mbr2.asm阻止程式跑飛

1.程式講解

mbr2.asm程式碼如下:

jmp $ ;$表示當前行的地址
times 510-($-$$) db 0 ;$$表示段開始的地址,$-$$表示當前行前面的程式碼佔用的位元組數。
db 0x55,0xaa

在Linux命令列中執行如下命令:

nasm mbr2.asm -o mbr2.bin
hexdump mbr2.bin -C
ndisasm mbr2.bin

從上面截圖中可以看到“jmp $”生成的機器碼是“0xeb,0xfe”,其中0xeb是操作碼,0xfe是運算元。這條指令中的運算元是當作有符號數處理的,0xfe換算成十進位制數是“-2”(負2)。而這條指令共2個位元組,當CPU讀取完這條指令後暫存器ip的值會加2,執行完這條指令後,暫存器ip的值會加負2。這樣的話CPU就會不斷的重複執行這條指令,程式就不會跑飛了。

2.程式演示

下面我們將mbr2.bin寫入到虛擬硬碟的第一個扇區。

dd conv=notrunc if=mbr2.bin of=/media/VMShare/GrapeOS.img
hexdump /media/VMShare/GrapeOS.img -C

然後用除錯模式觀察一下。

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img -S -s

前面我們介紹過,這裡GDB是按32位反彙編的,16位反彙編是有問題的。但有些彙編程式碼在16位和32位下生成的機器碼是一樣的,比如這裡的jmp $。所以這裡的反彙編也可以適當參考一下。從上面截圖上可以看到,每次單步執行後,程式地址仍然停留在0x7c00。這就是透過死迴圈來防止程式跑飛的辦法。
下面我們來刪除斷點,讓程式正常執行。
首先來檢視斷點:

(gdb) i b

刪除斷點:

(gdb) d 斷點編號

然後讓程式繼續執行:

(gdb) c

截圖如下:

mbr2.asm雖然解決了程式跑飛的問題,但CPU佔用率仍然高,筆記本風扇還是呼呼的轉。下面我們來徹底解決這個問題。

四、mbr3.asm徹底解決CPU佔用率高的問題

mbr3.asm的程式碼如下:

stop:
hlt ;使CPU暫停執行,直到有中斷髮生。(降低CPU使用率)
jmp stop 

times 510-($-$$) db 0
db 0x55,0xaa

上述程式碼主要用了一個hlt指令,讓CPU暫停,如果有中斷髮生,會執行下一行jmp stop,然後又執行hlt。這樣不僅防止了程式跑飛,而且降低了CPU使用率。
有了前面的基礎,我們這次編譯執行一氣呵成。
在Linux命令列中執行:

nasm mbr3.asm -o mbr3.bin
dd conv=notrunc if=mbr3.bin of=/media/VMShare/GrapeOS.img

在Windows命令列中執行:

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img

截圖如下:

從上圖工作管理員中可以看到,QEMU的CPU佔用率已經降下來了。


本講影片版地址:https://www.bilibili.com/video/BV1io4y1i7GP/
本教程程式碼和資料:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS作業系統QQ群:643474045

相關文章