跟我一起寫作業系統(一)——10分鐘寫個作業系統
專案地址:https://github.com/lucasysfeng/lucasOS
想動手,但不知從何入手,是學習一門新知識普遍會遇到的尷尬點。筆者喜歡邊實踐邊學習理論,筆者的寫作思路是:入門的文章要避免講一些高深的理論,而應該先丟擲demo,從研究demo入手,逐步加深demo的難度,從而學習這個過程中涉及到的理論知識。下面就讓我們花10分鐘寫個“作業系統”。
第一節 開發環境
我們在linux下製作軟盤、編譯核心等,因此需要linux開發環境。如果你用windows, 那麼在windows下安裝VMware, 在VMware中安裝ubuntu虛擬機器,此ubuntu作為開發環境。
注:筆者的開發環境是windows--VMware--ubuntu14.04.
第二節 計算機啟動過程
寫作業系統看似是一個複雜的過程,但只要我們將過程分解,完成每一步,那麼完成一個作業系統就是水到渠成的事了。好了,我們就看一下計算機的啟動過程,看作業系統何時被啟動的。
第一步:讀取BIOS
按下電源按鈕後,計算機首先讀取一塊ROM晶片,這塊晶片裡的程式是"基本輸入輸出系統"(Basic Input/Output System),即BIOS.
第二步:硬體自檢
BIOS會檢查計算機硬體是否滿足執行條件,如果硬體出現問題,主機板會發出不同含義的蜂鳴,啟動中止。
第三步:啟動順序
硬體檢查完成後,BIOS會將控制權交給下一階段的啟動程式,注意,“下一階段的啟動程式”可能存放在硬碟中,也可能存放在CD/DVD中,或者軟盤中等等,可以設定BIOS選擇從哪個裝置啟動。
第四步:主引導記錄
BIOS找到了“下一階段的啟動程式”所在裝置,會讀取該裝置的第一個扇區,即讀取最前面的512位元組,稱為主引導記錄。主引導記錄會告訴計算機下一步到哪裡去找作業系統。
第五步:bootloader
計算機讀取"主引導記錄"前面446位元組的機器碼之後,執行事先安裝的“啟動管理器”bootloader,由使用者選擇啟動哪個作業系統。如果你安裝了多個作業系統,那麼就要從這步做出選擇了。
第六步:載入核心
好了,選擇作業系統(核心)後,會載入核心,下面就交給核心去處理了。
第三節 主引導記錄
我們使用虛擬機器來啟動作業系統,上面的第一步和第二步我們不做,由虛擬機器去完成;第三步“啟動順序”我們選擇從軟盤啟動(我們用映象代替,並不是真的軟盤),需要對虛擬機器做下設定,選擇從軟盤啟動。下面重點來看第四步,我們寫一下“主引導記錄”,讓BIOS讀取我們寫的主引導記錄。
1. 主引導記錄程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
; 檔名 boot.asm
org 7c00h ; BIOS讀入MBR後,從0x7c00h處開始執行
; 下面部分和10h有關中斷,10h中斷用來顯示字元
mov ax, cs
mov es, ax
mov ax, msg
mov bp, ax ; ES:BP表示顯示字串的地址
mov cx, msgLen ; CX存字元長度
mov ax, 1301h ; AH=13h表示向TTY顯示字元,AL=01h表示顯示方式(字串是否包含顯示屬性,01h表示不包含)
mov bx, 000fh ; BH=00h表示頁號,BL=0fh表示顏色
mov dl, 0 ; 列
int
10h
msg: db
"hello world, welcome to OS!"
msgLen: equ $ - msg ; 字串長度
times 510 - ($ - $$) db 0 ; 填充剩餘部分
dw 0aa55h ; 魔數,必須有這兩個位元組BIOS才確認是MBR
|
2. 編譯
# nasm boot.asm -o boot.bin
如果沒有nasm,安裝它 sudo apt-get install nasm, 執行完上述命令,會生成boot.bin檔案,這就是我們的主引導記錄二進位制。
第四節 程式碼解釋
我們再來看下主引導記錄的彙編程式碼,熟悉彙編的讀者可忽略本節。
1. 為什麼MBR要從0x7c00h處開始執行?
ORG是偽指令,org 7c00h是告訴編譯器,下面程式碼裝入到記憶體的起始地址0x7c00h處。為什麼呢,這是因為BIOS讀取主引導記錄後,會從0x7c00h處開始執行,那麼BIOS為什麼會從0x7c00h這個地址開始執行,而不是其他地址呢,這一切都要從大明湖畔的8086cpu說起。
時光飛逝,容顏易老,8086卻還是那個樣子,如圖所示:
圖 8086實物圖
圖 8086引腳圖
正如圖中所示,8086cpu的地址匯流排寬度為20(AD0-AD19),可以傳送220的地址資訊,即可以定位220(1M)的記憶體地址空間,那麼這1M的記憶體地址空間是如何分配的呢,見下圖所示(圖是386的,我們目前只關心真實模式即1M記憶體地址空間分配):
圖 真實模式記憶體地址空間分佈
看到0x7c00h了嗎?0x0000h--0x7c00h這一段存的是BIOS中斷向量和一些BIOS資料等,至於到底為什麼以0x7c00h為界,本文不做討論,有興趣看這裡http://www.glamenv-septzen.net/en/view/6。
2. int 10h是幹嘛的?
當出現int 10h中斷時,表示要操作顯示器了,此時AH暫存器表示如何顯示,程式碼中的AH為13h,表示要在TTY(偽終端)顯示字元,此時其他幾個暫存器都有一定的含義,如下所示:
ES:BP -- 顯示字串的地址 CX -- 顯示字串的長度
BH -- 頁碼 BL -- 屬性(若AL=00H或 01H)
DH -- 行 DL -- 列
AL -- 顯示輸出方式
下面一段程式碼也就不難理解了:
1 2 3 4 5 6 7 8 9 mov ax, cs
mov es, ax
mov ax, msg
mov bp, ax ; ES:BP表示顯示字串的地址
mov cx, msgLen ; CX存字元長度
mov ax, 1301h ; AH=13h表示向TTY顯示字元,AL=01h表示顯示方式(字串是否包含顯示屬性,01h表示不包含)
mov bx, 000fh ; BH=00h表示頁號,BL=0fh表示顏色
mov dl, 0 ; 列
int
10h
3. $和$$是什麼意思?
$ 是當前位置
$$ 是段開始位置下面兩句就不難理解了:
1 2 msgLen: equ $ - msg ; 字串長度
times 510 - ($ - $$) db 0 ; 填充剩餘部分
4. 為什麼要有0xaa55h魔數?
BIOS檢查完硬體後,會尋找下一個裝置來啟動計算機,BIOS找到一個裝置後,會讀取該裝置的第一個扇區,也就是讀取最前面的512個位元組。如果這512個位元組的最後兩個位元組是0x55和0xAA,表明這個裝置可以用於啟動;如果不是,表明裝置不能用於啟動,控制權於是被轉交給"啟動順序"中的下一個裝置。
第五節 製作軟盤映象,加入主引導記錄
如何用dd命令製作軟盤,自行google之。
1. 首先,我們製作一個空的軟盤映象empty.img:
# dd if=/dev/zero of=empty.img bs=512 count=2880
2. 之後,我們製作一個包含主引導記錄boot.bin的映象檔案lucasOS.img:
# dd if=boot.bin of=lucasOS.img bs=512 count=1
3. 然後,將empty.img中1個扇區後的資料拷貝到lucasOS.img的後:
# dd if=empty.img of=lucasOS.img skip=1 seek=1 bs=512 count=2879
這樣就做成了一個大小為1.44Mb的包含主引導記錄的軟盤映象檔案lucasOS.img。
4. 將虛擬機器ubuntu中的檔案lucasOS.img拷貝到windows下(滑鼠直接拖拽,如果不行google之)。
第六節 用軟盤映象lucasOS.img啟動一個空的虛擬機器
1. VMware建立空的虛擬機器,去掉開機從CD/DVD啟動選項。
2. 網路選擇host-only模式。
3. 選擇從軟盤驅動,路徑選擇上一節已經拷貝到windows下的映象lucasOS.img.
4. 開啟虛擬機器電源,看到如下畫面,恭喜你,成功了。
好了,至此,我們完成了主引導記錄,後續會討論載入核心,並進一步討論程序管理、記憶體管理、檔案系統和中斷等等。
程式碼獲取
本系列GitHub地址 https://github.com/lucasysfeng/lucasOS.git
獲取程式碼:
# git clone https://github.com/lucasysfeng/lucasOS.git
本講的程式碼是code/chapter1,筆者已經將上面的命令整合到Makefile中了,讀者只需進入目錄,按ReadMe.txt執行即可。有問題請留言。