(幾年前寫的部落格,貼過來,再更新了些細節)
可以用gcc編譯個binary,然後用grub呼叫執行。
首先,為了方便執行和除錯我們需要一個虛擬機器。虛擬機器有很多選擇,這裡用最簡單的qemu。
先用dd建立一個檔案作為虛擬盤,100MB就可以了:
然後對這個虛擬磁碟進行分割槽:
用命令n建立一個分割槽就可以了。通常情況下分割槽的起始扇區是2048。用命令w把變動寫入虛擬盤。
把這個分割槽虛擬成裝置檔案:
這裡指定了起始扇區的偏移量。2048個扇區,每個扇區512個位元組,總共是1048576位元組。
格式化:
掛載起來,這樣就可以方便地往裡面放kernel和grub需要的配置檔案和模組什麼的:
安裝grub:
使用qemu來啟動虛擬機器:
這時候應該能夠看到grub的提示符了。當然現在還沒有grub選單也沒有kernel,我們暫時先關掉虛擬機器。
接下來可以為grub建立個multiboot啟動選單:
製作選單命令:
確保資料寫回了虛擬盤:
這時候如果你再開啟虛擬機器,應該就可以看到啟動選單了,當然因為還沒有kernel,選擇選單項後無法繼續,會提示kernel找不到。
下一步,我們用c語言從頭編寫個最簡單kernel程式。這個kernel沒有實現作業系統的基本功能。但是可以被grub裝載和執行。
kernel.c:
有必要再寫個連結模板,確保編譯好的kernel裝載在記憶體地址0x100000,這裡是grub程式碼最後跳轉到的區域,從這裡我們的kernel接過了接力棒。
kernel.ld:
還有一個Makefile,主要是設定一些編譯選項。
Makefile:
編譯生成kernel並放入我們的虛擬盤裡:
再次啟動虛擬機器,在啟動選單裡選擇multiboot我們的kernel,應該就能看到Hello World!的字元顯示在虛擬機器螢幕上了。
如果想要除錯,可以執行gdb。因為我們在啟動qemu的時候使用了-s選項,所以qemu預設會開啟tcp埠1234作為gdb除錯埠。在gdb中可以使用target remote tcp::1234命令來連線。試試看連線,會發現cpu一直在執行0x100066處的指令。用objdump -D kernel看下kernel的彙編程式碼:
0x100066處的指令正好是死迴圈的那條jmp指令。
(以上在Arch Linux上透過。使用的grub版本是2.02-beta2。)
(幾年前寫的部落格,貼過來,再更新了些細節)
可以用gcc編譯個binary,然後用grub呼叫執行。
首先,為了方便執行和除錯我們需要一個虛擬機器。虛擬機器有很多選擇,這裡用最簡單的qemu。
先用dd建立一個檔案作為虛擬盤,100MB就可以了:
然後對這個虛擬磁碟進行分割槽:
用命令n建立一個分割槽就可以了。通常情況下分割槽的起始扇區是2048。用命令w把變動寫入虛擬盤。
把這個分割槽虛擬成裝置檔案:
這裡指定了起始扇區的偏移量。2048個扇區,每個扇區512個位元組,總共是1048576位元組。
格式化:
掛載起來,這樣就可以方便地往裡面放kernel和grub需要的配置檔案和模組什麼的:
安裝grub:
使用qemu來啟動虛擬機器:
這時候應該能夠看到grub的提示符了。當然現在還沒有grub選單也沒有kernel,我們暫時先關掉虛擬機器。
接下來可以為grub建立個multiboot啟動選單:
製作選單命令:
確保資料寫回了虛擬盤:
這時候如果你再開啟虛擬機器,應該就可以看到啟動選單了,當然因為還沒有kernel,選擇選單項後無法繼續,會提示kernel找不到。
下一步,我們用c語言從頭編寫個最簡單kernel程式。這個kernel沒有實現作業系統的基本功能。但是可以被grub裝載和執行。
kernel.c:
有必要再寫個連結模板,確保編譯好的kernel裝載在記憶體地址0x100000,這裡是grub程式碼最後跳轉到的區域,從這裡我們的kernel接過了接力棒。
kernel.ld:
還有一個Makefile,主要是設定一些編譯選項。
Makefile:
編譯生成kernel並放入我們的虛擬盤裡:
再次啟動虛擬機器,在啟動選單裡選擇multiboot我們的kernel,應該就能看到Hello World!的字元顯示在虛擬機器螢幕上了。
如果想要除錯,可以執行gdb。因為我們在啟動qemu的時候使用了-s選項,所以qemu預設會開啟tcp埠1234作為gdb除錯埠。在gdb中可以使用target remote tcp::1234命令來連線。試試看連線,會發現cpu一直在執行0x100066處的指令。用objdump -D kernel看下kernel的彙編程式碼:
0x100066處的指令正好是死迴圈的那條jmp指令。
(以上在Arch Linux上透過。使用的grub版本是2.02-beta2。)