X86架構作業系統核心實現過程
#作業系統核心實現(一) 作業系統的學習還是需要落地不能停留在概念上,於是打算模仿並實現作業系統核心,前面一直在做川合秀實先生的《30天自制作業系統》,但是由於它的底層系統是windows,以及講解較為淺顯不能深入瞭解具體的核心執行模式狀態,轉到了劉歡師兄寫的《X86架構作業系統核心實現》上,部落格雖短只有10篇左右,但內容充實,不斷的帶入新的文章新的連結,在做核心的途中也遊覽了許多計算機各方面知識的風景,收益頗豐。 ###開發工具 本次開發使用的都是常見且基礎的工具
- 開發語言為少量的彙編,大部分為C
- 彙編編譯器選擇nasm
- C編譯器採用gcc
- 連結器ld
- 虛擬機器當然是linux下常用的qemu
###開發過程 做事情之前首先要明確我們具體要做什麼,我們要做的是一個從0開始的作業系統,從0指的是從通電的一瞬間開始,首先我們必須要讓計算機能夠載入我們所做的OS,那麼我們不得不瞭解計算機的具體啟動過程。 下面這張圖是我通過閱讀《計算機是如何啟動的》畫出的。
- 這張圖對計算機的啟動過程描述的十分詳細了,細微的地方不再講解,我們的重心是做出一個OS,但有一點我們需要注意,當BIOS讀取裝置第一扇區後,除了常規的直接載入Bootloader外,我們可以選擇使用GRUB(來自GNU的多作業系統啟動程式)來直接載入作業系統核心,因為寫Bootloader需要太多的硬體知識偏離了我們的重心,同時自己動手實現Bootloader會造成與已有OS的不相容。
- GRUB提供了multiboot規範,符合這個協議的作業系統核心能被GRUB識別,並按照定義的規則被載入。 ####開發過程中所用到的指令碼檔案 開發過程中我們需要使用指令碼檔案來幫助進行編譯連結
- 首先是我們熟悉的Makefile,我們需要它來編譯我們的整個核心,而這個Makefile在整個開發過程中幾乎可以全程使用,來看下劉歡師兄所寫的Makefile指令碼。
#!Makefile
C_SOURCES = $(shell find . -name "*.c")
C_OBJECTS = $(patsubst %.c, %.o, $(C_SOURCES))
S_SOURCES = $(shell find . -name "*.s")
S_OBJECTS = $(patsubst %.s, %.o, $(S_SOURCES))
CC = gcc
LD = ld
ASM = nasm
C_FLAGS = -c -Wall -m32 -ggdb -gstabs+ -nostdinc -fno-builtin -fno-stack-protector -I include
LD_FLAGS = -T scripts/kernel.ld -m elf_i386 -nostdlib
ASM_FLAGS = -f elf -g -F stabs
all: $(S_OBJECTS) $(C_OBJECTS) link update_image
.c.o:
@echo 編譯程式碼檔案 $< ...
$(CC) $(C_FLAGS) $< -o [email protected]
.s.o:
@echo 編譯彙編檔案 $< ...
$(ASM) $(ASM_FLAGS) $<
link:
@echo 連結核心檔案...
$(LD) $(LD_FLAGS) $(S_OBJECTS) $(C_OBJECTS) -o hx_kernel
.PHONY:clean
clean:
$(RM) $(S_OBJECTS) $(C_OBJECTS) hx_kernel
.PHONY:update_image
update_image:
sudo mount floppy.img /mnt/kernel
sudo cp hx_kernel /mnt/kernel/hx_kernel
sleep 1
sudo umount /mnt/kernel
.PHONY:mount_image
mount_image:
sudo mount floppy.img /mnt/kernel
.PHONY:umount_image
umount_image:
sudo umount /mnt/kernel
.PHONY:qemu
qemu:
qemu -fda floppy.img -boot a
.PHONY:bochs
bochs:
bochs -f tools/bochsrc.txt
.PHONY:debug
debug:
qemu -S -s -fda floppy.img -boot a &
sleep 1
前面一部分是一些shell所寫的定位目標檔案,以及gcc與ld的配置,值得一提的是ld的連結命令中-nostdlib 是不連結C語言的標準庫,因為我們目前並沒有函式庫,否則也不能稱之為一個從0的作業系統了,在看程式碼的過程中碰到了patsubst這個函式,這裡它是把.c與.s檔案轉換成了.o檔案,那為什麼可以直接轉換呢?變成.o檔案難道不需要編譯了嗎?Google了一下發現他是替換萬用字元,即只替換字尾,我覺的在這裡把. s與.c檔案都換成.o檔案應該是想劃定範圍,用範圍內的這些專案連結掛載在kernel下的檔案把。最後是一些檔案掛載和虛擬機器qemu的映象製作等。我們應該能注意到.PHONY這個命令,這個命令的意思是告訴Make,總是要執行它後面的命令,也就是說始終認為它後面生成的檔案沒有更新需要生成最新的。
- 接下來是ld連結器的指令碼。這個指令碼主要是告訴ld程式如何構造我們所需的核心映像檔案,此指令碼暫時難以搞懂,我們明白了他的作用就好,其中部分需要在之後的除錯資訊中才能明白,我們先向下接著走。
- 小結:所以我們現在要做的是使用Makefile與ld編譯連結我們的核心檔案,接著使用qemu生成映象檔案,按照Multiboot生成規範格式的引導資訊,同時使用標準ELF格式,GRUB就會將我們所寫的核心正確載入與執行。 當前我們的目錄結構是這樣:
|-- Makefile
`-- scripts
`-- kernel.ld
####啟動映象 打算在一個虛擬軟盤上放入我們的核心,劉歡師兄部落格中寫到給出了軟盤映象但並沒有找到,所以準備自己先嚐試做一做帶有GRUB的軟盤映像。先明確一下步驟,製作軟盤,接著在這個軟盤上安裝GRUB。
- 製作軟盤在linux下比較簡單,使用dd命令,我們首先建立兩個軟盤映象。
#dd if=/dev/zero of=auxiflp.img bs=512 count=2880;
#dd if=/dev/zero of=bootflp.img bs=512 count=2880;
接著從ftp://alpha.gnu.org/gnu/grub/下載GRUB的編譯好的檔案,當然也可以選擇下原始檔自己編譯後再用。解壓後將stage1,stage2提取出來用,剛才建立的兩個軟盤映象auxiflp.img用來安裝GRUB檔案,bootflp。img是我們最終要得到的引導盤,接著我們要把stage1,stage2寫入auxiflp.img中。
#dd if=stage1 of=auxiflp.img bs=512 count=1;
#dd if=stage2 of=bootflp.img bs=512 seek=1;
這樣就完成了auxiflp.img。接著我們對bootflp.img進行操作,首先把它格式化為我們常見的FAT12格式,新建資料夾用來掛載軟盤映像等。
#mkfs.vfat -F12 bootflp.img ;
#mkdir floppy ;
#mount -o loop bootflp.img floppy ;
#mkdir -p floppy/boot/grub
#cp stage* floppy/boot/grub ;
我們現在明確auxiflp.img中裝好了GRUB,bootflp.img已經做好了一切準備,接下來的工作是用auxiflp幫助bootflp.img完成GRUB的安裝,由於對qemu的使用不熟練,此處還在不斷嘗試中。
- 完成了含有GRUB的軟盤製作後,我們可以說做好了所有的鋪墊工作,接下來的就是核心的程式碼工作了。