第一個Hello,OS World操作系統
轉載自 http://www.tsingfun.com/html/2015/dev_0804/hello_os_word_my_first_os.html
首先闡述下程序運行的基本原理:計算機CPU只執行二進制指令,我們使用的開發語言開發出的程序最終由相應的編譯器編譯為二進制指令,二進制中包含程序相關的數據、代碼指令(用我們最常見的公式描述就是:程序=數據+算法)。CPU讀取相應的指令、數據後開始執行,執行後的結果輸出到外部設備,如屏幕、磁盤等。整個過程中,CPU發揮最為核心的作用,與其他設備一起完成程序的執行、輸出。
OS本身也是程序,它的運行也是如此,開機後從指定地址處(0x7c00),開始執行指令。先看看本節例子最終運行效果:
編譯運行環境:
nasm:Inter x86匯編編譯工具,用戶將我們的匯編代碼編譯為二進制。(下載地址 http://www.tsingfun.com/html/2015/soft_0804/nasm_asm.html)
Bochs:運行os的虛擬機工具,模擬加載我們生成的軟盤映像,並運行os。(下載地址 http://www.tsingfun.com/html/2015/soft_0804/Bochs_Lightweight_VirtualMachine.html)
代碼如下:
;-------------------------------------------------------------- ; 平凡OS(Pf OS) 一個最簡單的OS ; Author : tsingfun.com ;-------------------------------------------------------------- ; FAT12引導扇區 ORG 0x7c00 ;引導開始地址,固定的,主板BIOS決定,本條指令只是申明,不占字節(有興趣的可以單獨編譯這條指令,然後查看二進制,文件0k) JMP _START ;CPU執行的第一條指令,就是跳轉到_START地址處(這裏是標簽,實際編譯後_START是有一個相對地址的) TIMES 3-($-$$) NOP ;NOP:一個機器周期。$:當前地址,$$:首地址。因為以上信息必須占3個字節,所以不足的部分用nop指令填充, ;具體nop占用幾個字節請讀者使用二進制查看工具自行驗證。 DB "PFOSBEST" ; 標識(公司、品牌等)8個字節 DW 512 ; 每扇區字節數 DB 1 ; 每簇扇區數 DW 1 ; Boot內容占幾個扇區 DB 2 ; 共有多少FAT表 DW 224 ; 根目錄文件數最大值 DW 2880 ; 扇區總數 DB 0xf0 ; 介質描述符 DW 9 ; 每FAT扇區數 DW 18 ; 每磁道扇區數 DW 2 ; 磁頭數(面數) DD 0 ; 隱藏扇區數 DD 2880 ; 若上面“扇區總數”為0,則這個值記錄扇區總數 DB 0,0,0x29 ; 中斷13的驅動器號;未使用;拓展引導標記 DD 0xffffffff ; 卷序列號 DB "PFOS v1.0.0" ; 卷標(11個字節) DB "FAT12 " ; 文件系統類型(8個字節) ;--------------------------------------------------------------------- ; 448個字節,引導代碼、數據及其他填充字符 TIMES 18 DB 0 _START: MOV AX, 0 ;AX:累加寄存器,CPU內置的16位寄存器,最為常用,可以用於存儲運行的中間結果,此處清零 MOV SI, MSG ;SI:(source index)源變址寄存器,常用來存儲待操作的數據的首地址,此處指向數據區的字符串 _LOOP: ;循環指令開始 MOV AL, [SI] ;[]取地址中的內容,AL是AX的低8位,所以取8bit,即1字節,字符串的ASCII。 ADD SI, 1 ;字符串往後移動一個字節 CMP AL, 0 ;判斷當前取出的ASCII是否是0, JE _END ;JE:Equal則Jmp,等於零表示字符串顯示完了,結束。 MOV AH, 0x0e ;調用系統10h中斷顯示ASCII字母,AH,BX指定顯示模式及參數(詳見:http://www.tsingfun.com/html/2015/dev_0804/570.html) MOV BX, 15 INT 0x10 JMP _LOOP ;繼續下一個字符的顯示 _END: JMP $ ;跳到當前的地址,當然就陷入無限循環啦,此處為了讓程序停在此處。 ;數據區,就是待輸出的字符串信息 MSG DB 0x0a, "----------------------------------------------", 0x0d, 0x0a, "| Hello, OS World! |", 0x0d, 0x0a, "----------------------------------------------", 0x0d, 0x0a, 0x0 TIMES 510-($-$$) DB 0 DW 0xaa55 ; 結束標誌 ;---------------------------------------------------------------------- ; FAT數據區 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 TIMES 4600 DB 0 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 TIMES 1469432 DB 0
其中,主要的步驟代碼中都有詳盡的註釋,如有任何問題,請移步至論壇《深入OS》板塊發帖討論。
編譯執行過程:
打開dos窗口,進入源碼所在目錄,執行命令nasm boot.asm -o pfos.img:
同目錄下生成一個"pfos.img"軟盤映像文件,接下來只需要把它裝載到虛擬機運行即可,當然有條件的話可以實際寫入老式的軟盤用真機運行,結果是一樣的。
同目錄下新建一個“pfos.bxrc” Bochs配置文件,內容如下:
#how much memory the emulated machine will have megs:4 #filename of ROM images romimage:file=$BXSHARE\BIOS-bochs-latest,address=Oxf0000 vgaromimage: file=$BXSHARE\VGABIOS-elpin-2.40 #what disk images will be used floppya:1_44="pfos.IMG",status=inserted #Choose the boot disk boot:a #where do we send log messages? #log:bochsout.txt
雙擊“pfos.bxrc”啟動Bochs運行即可啟動我們自己寫的os了。
源碼下載:http://www.tsingfun.com/uploadfile/2016/0628/hello os world.zip
接下來解釋一下運行原理:
首先,軟盤大小是1.44M(這個是固定的),所以我們在程序中指定它為1,474,560 字節,除了程序本身的指令、數據外,不足的部分全部補零。
TIMES 1469432 DB 0 就是此處開始寫1469432個字節的0。
軟盤采用的是FAT12文件格式,我們現在的常見的文件格式有FAT32、NTFS、EXT3等,FAT12是早期的一種文件格式。文件格式是文件格式化存儲的一種算法,比如我們要將一個文件存儲到軟盤(磁盤)上,有些人可能會想我直接從地址0開始存儲,直到結束,那麽文件名、文件大小、創建時間等其他信息怎麽存?緊接著後面繼續存儲麽?那該給各部分分配多少字節空間?先不說後續查找文件的效率,這種存儲方法無章可循會完全失控,是不行的方案。
文件格式化算法就解決了此類問題,而且兼顧文件的高效率查找。基本原理就是給軟盤(磁盤)分區:FAT區、目錄區、數據區,存儲文件時先存儲文件基本信息到目錄區,然後文件的數據按照一定格式存儲到數據區,目錄區中有數據區中文件數據的地址。
這裏只簡單介紹一下FAT12格式,後續篇章會深入解析每個字節代表的含義。
我們來看看我們生成的映像裏面到底有什麽東西?這時我們需要用到二進制查看工具WinHex,點此下載 http://www.tsingfun.com/html/2015/soft_0804/WinHex.html 。
以上看到的是二進制靜態代碼,實際運行中各指令的地址都是動態變化的,下來一起借助Bochs的debug功能來一探究竟。
我們雙擊“pfos.bxrc”默認是以運行模式啟動Bochs,實際上我們應該啟動bochsdbg.exe,因此寫個簡單的批處理腳本啟動它吧,如下:
@echo off
SET BXSHARE=C:\Program Files (x86)\Bochs-2.5
if %PROCESSOR_ARCHITECTURE% == x86 (
SET BXSHARE=C:\Program Files\Bochs-2.4.6
)
"%BXSHARE%"\bochsdbg -q -f "pfos.bxrc"
雙擊腳本,啟動debug模式,如下:
Bochs常用的debug命令如下:
b 0x... 斷點命令,指定地址處調試
info break 顯示當前斷點信息
c 繼續執行
s 步入執行
n 單步執行
info cpu 查看cpu寄存器(可分別執行 r/fp/sreg/creg)
print-stack 打印堆棧
xp /長度 地址 顯示地址處內容(xp:物理地址,x:線性地址)
u 起始地址 結束地址 反匯編一段代碼
trace on 反匯編執行的每條指令
trace-reg on 每執行一條指令都打印一下cpu信息
exit 退出調試
大家有興趣的話可以調試下,然後看看每步驟寄存器值的變化。
總結:本篇主要是讓大家對操作系統有個整體概念上的認識,揭開os神秘的面紗,從底層調試到運行,每個過程都真真切切展現在大家面前。至於匯編指令、地址尋址暫時不懂的話,也不要緊,後續章節會繼續作詳細闡述,力求使大家在不斷的運行、調試過程中逐漸熟悉並掌握匯編及計算機底層技術。
第一個Hello,OS World操作系統