自己動手從零寫桌面操作系統GrapeOS系列教程——12.QEMU+GDB調試

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


寫程序不免需要調試,寫不同的程序調試方式也不同。如果做應用軟件開發,相應的程序調試方式是建立在有操作系統支持的基礎上的。而我們現在是要開發操作系統,如何調試操作系統的程序呢?如果操作系統程序直接跑在真機上或虛擬機上(比如VirtualBox)是很難調試的,所以我們在開發階段操作系統程序主要在虛擬機QEMU上跑,因爲QEMU支持調試。當然很多事情都是有利也有弊的,QEMU雖然支持調試,但它的運行效率比VitrualBox要低,所以我們最終的GrapeOS程序是跑在VirtalBox上的。QEMU需要結合GDB才能實現調試,下面我們一起來學習一下。

一、QEMU調試模式

在Windows的cmd命令行中輸入如下一行命令:

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

上面這行命令比之前多了兩個參數,“-S”表示讓CPU在將要執行第一條指令前暫停,“-s”表示讓QEMU打開自帶的GDB服務端功能,且網絡端口號是1234。截圖如下:

執行上面的命令後,會彈出QEMU的窗口:

從上圖中可以看到QEMU窗口中間顯示一行文字“Guest has not initialized the display(yet).”,此時QEMU已進入調試模式。當QEMU進入調試模式後,就在等待GDB客戶端來連接它。當GDB客戶端連接上QEMU的GDB服務端就可以調試了。就像我們用PowerShell連接到CentOS就可以在PowerShell中操縱CentOS一樣,此時PowerShell是客戶端,CentOS是服務端。下面我們來介紹GDB客戶端。

二、GDB調試

GDB分爲服務端和客戶端,單說GDB,一般是指GDB客戶端。GDB是Linux中的一個調試軟件,所以我們準備在CentOS中使用它。首先我們通過PowerShell登錄CentOS。

1.安裝GDB

首次使用GDB可能需要安裝一下:

yum install gdb

2.啓動GDB

敲命令gdb就運行了,如下圖:

3.GDB連接到QEMU

在GDB中輸入如下命令連接QEMU:

target remote 192.168.10.102:1234

上面這行命令中的IP地址“192.168.10.102”是我的Windows的IP地址,你需要替換成你的Windows的IP地址。截圖如下:

如上截圖所示,我們已經通過GDB連接到QEMU了。圖中倒數第二行的十六進制數“0x0000fff0”表示CPU將要執行的指令地址。還記得前面介紹的實模式下1M內存的佈局嗎?這個地址在BIOS中,是CPU執行的第一條指令所在的地址。

4.設置斷點

設置斷點是調試必備的一個功能,比如我們在0x7c00處設置個斷點:

b *0x7c00

這樣就設置好了一個斷點。可以用同樣的方式設置多個斷點。

5.繼續運行

這個命令簡單,只有一個字母“c”,然後回車即可讓CPU繼續運行,當遇到斷點時會自動暫停。截圖如下:

6.查看寄存器

查看所有寄存器的命令是i r,截圖如下:

從上圖中可以看到此時CPU中很多寄存器的值,有朋友可能會有個疑問,以前學的寄存器是“ax、bx、cx……”這些,上面截圖中怎麼是“eax、ebx、ecx……”呢?原因是當年8086CPU的寄存器都是16位的,也就是“ax、bx、cx……”這些,很多講x86彙編語言的資料都只講了8086下的情況。而我們現在啓動的是32位x86模擬器“qemu-system-i386”,所有通用寄存器多了一個字母“e”表示擴展,從16位擴展成了32位。這些32位通用寄存器中的低16位就是原來的16位寄存器,比如eax的低16位還是ax,ah和al仍然表示ax的高8位和低8位,其它寄存器也一樣。這就是兼容,能讓舊程序在新CPU上運行。之前的16位寄存器中只有段寄存器沒有擴展,還是16位的,而且還增加了2個,分別是fs和gs。增加的這2個段寄存器作用和es基本一樣,之所以增加是怕在複雜的程序中出現段寄存器不夠用的情況。當數據比較多的時候GDB一般只輸出一部分,此時如果按回車鍵還會顯示出其它一些寄存器,但我們用不上,按“q”鍵退出繼續輸出即可。

下面來看一下如何查看單個寄存器,比如我們要查看寄存器ax的值,輸入命令p $ax,如果想以十六進制顯示可以輸入命令p /x $ax,截圖如下:

7.查看內存

命令格式:x /nfu addr
n表示數量
f表示格式:x(hex), d(decimal), c(char)等。
u表示顯示單位:b(byte), h(halfword), w(word), g(giant, 8 bytes)。
下面我們分別演示查看0x7c00開始的8個單字節、8個雙字節、8個四字節、8個八字節的內存值。截圖如下:

雖然目前看到的數據都是0,但我們以後寫上程序就不一樣了。

8.反彙編

有時候需要將機器碼反彙編成彙編代碼方便查看,下面我們以反彙編0x7c00開始的10個字節爲例:

disas 0x7c00,+10

截圖如下:

上面截圖中顯示的彙編代碼是GDB默認的AT&T語法,我們可以設置改成Intel語法:

set disassembly-flavor intel

截圖如下:

需要說明一下的是這裏的反彙編結果是錯的。因爲它是按照32位模式反彙編的,而我們現在還處在16位實模式中,所以這個反彙編功能只能等後面我們進入32位模式纔有用。至於反彙編16位代碼我們會在後續教程中介紹其它方法。

9.執行下一條指令

在調試的時候有時需要一條指令一條指令的單步執行,單步執行的命令是si

從上面的截圖可以看到,每輸入一個si回車,就會執行一條命令。每個si命令下面一行中的十六進制數表示下一條指令的地址,可以看到地址在不斷增加,說明的確在執行指令。如果想知道每一步都執行了什麼指令,可以用下面這個命令來反彙編下一條要執行的指令:

set disassemble-next-line on

從上面截圖中可以看到每一步的指令,但這個反彙編結果也是錯的,原因和上面的一樣。
順便介紹個小技巧,如果不輸入命令直接回車會重複上一個GDB命令,就像上圖中最後兩步,什麼命令都沒有直接回車就表示重複執行si這個GDB命令。

10.退出GDB

本講最後介紹的指令是退出GDB,非常簡單,輸入q,然後再輸入y即可。 截圖如下:

退出GDB後就又回到進入GDB前的Linux命令行環境中了。

三、退出GDB後的問題

如果大家按照上面順序做實驗,退出GDB後,CPU佔用率會比較高,和上講中的情況一樣,直接關閉QEMU窗口即可。這個問題我們在下一講中解決。


本講視頻版地址:https://www.bilibili.com/video/BV18G4y1P7CU/
本教程代碼和資料:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS操作系統QQ羣:643474045

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章