作者:丁宋濤
系統啓動過程概述
在掀下電腦開機按鈕後,電源就會開始向主板和其他外圍設備供電。初始狀態下的電壓還不太穩定,因此並不會立即開始指令的執行。此時,主板上的控制芯片組會發出重置信號,然後等待內部初始化工作的完成。等到控制芯片組檢測到電源己經開始穩定供電後,它撤去信號,跳轉到合適的內存地址處,讀取並執行第一條機器指令。系統需要完成一系列的準備工作,以確保後續系統及程序的正確執行。這些準備工作包括檢測基礎的外圍設備是否存在、檢測系統實際物理內存及程序可用內存大小、建立內存空間的映射圖、對硬件設備發出初始化信號等等。這些工作應當完成於操作系統內核運行之前,將軟硬件調整到合適的狀態下,爲最終操作系統內核的調入架設好正確的外圍環境。而執行該工作的程序就被稱爲引導加載程序。
當操作系統能夠運行系統服務及用戶程序時,或者說計算機可以與用戶進行交互時,系統啓動過程就被認爲是完成了。典型的開機啓動時間大約爲一分鐘,這包括了上電自檢、引導時間和操作系統與其他相關軟件的裝載和初始化時間。嵌入式系統,如電視、通信設備等,通常要求更快速的,甚至是瞬時的啓動時間。對於這些系統而言,相關功能可以被製造廠商直接燒在存儲器中,從而略去了較複雜的引導加載程序的設計。
引導加載程序的作用
主板上的中央處理單元,即CPU,只能運行貯存在只讀存儲器上的程序代碼。在啓動階段時,系統被認爲處於一個“完全空白”的狀態下,即在存儲器上的數據要麼是空白的,要麼是由於前次的系統崩潰而殘留下的不合法代碼。而現代操作系統的設計日趨複雜,代碼量日趨龐大,將其固化在造價昂貴的存儲器中顯然是不合理不科學的。操作系統與應用程序的鏡像與數據一般存放在一些非易失性的存儲介質上,典型如硬盤、光盤、U盤等設備中。因此,需要調用引導加載程序,將這部分數據和代碼加載到存儲器中,以備後續執行。相較於完整的操作系統鏡像,引導加載程序的代碼量要小得多,且訪問的數據量是不多的。
此外,爲了執行過程中操作系統與外界能夠正常交互,系統需要提供給操作系統一份完備的系統環境信息,這就需要引導加載程序在啓動階段就完成對外設的探測工作。現在的硬件設備在設計與實現時,往往會板載一塊ROM,保存自身的啓動代碼,用於在接收到系統發來的啓動信號時能夠正確響應,完成自身的初始化工作,並在完成後向系統發送回中斷信號。引導加載程序將會按照硬件規範所提供的端口,向其發送正確的信號或硬件命令,提醒它們及時完成自身的啓動。
引導加載程序的設計
在系統上電或復位後,幾乎所有的都是從芯片製造商預先規定的地址上取出並執行第一條指令的。個人電腦往往會在計算機主板芯片上固化一個小程序,保存系統初始化設置信號、開機自檢等信息。這段程序稱爲BIOS,即基本輸入輸出系統,它與硬盤中的操作系統引導加載程序協同工作,一起完成整個引導加載程序的功能。
個人pc機的操作系統引導介紹
首先,我們看一下操作系統啓動部分的執行流程。當PC的電源打開以後,80X86結構的CPU將自動進入實模式,並從地址0XFFFF0開始自動執行程序代碼,這個地址通常是BIOS中的地址。PC機的BIOS將執行某些系統的硬件檢測,並在物理地址0處開始初始化中斷向量。此後,計算機將可啓動設備的第一個扇區(磁盤引導扇區,512字節)讀入內存絕對地址0x7C00處,並跳轉到這個地方。
現在假定機器在BIOS自檢完成後,選擇從軟盤啓動,那麼計算機就會檢查軟盤的0面0磁道1扇區,如果發現磁盤以0xAA55結束,則計算機就會認爲這是一個引導扇區,此時計算機將這512個字節的內容裝載到內存地址0000:7c000處,然後跳轉到0000:7c000處講控制權徹底交給這段引導代碼。從此,計算機不再由固化在主板上的BIOS的程序控制,而變成由操作系統的一部分來控制。
從計算機組成原理看操作系統引導
大約一個操作系統源代碼中通常就會包含大約10%的起關鍵作用的彙編語言代碼。爲了便於大家理解,我們從最爲熟悉的桌面PC平臺,開始講解。
首先讓我們看一下,下面一段的彙編代碼:
org 07c00h
mov ax, cs
mov ds, ax
mov es, ax
上述的彙編代碼完成這樣的功能,其中彙編指令org 07c00h就是告訴編譯器程序,將這段代碼加載到7c00處,這樣在計算機自檢完成後就可以訪問我們的程序了。那麼,我們考慮一下,我們如何驗證,我們的代碼被計算機讀取並開始工作呢?如果,能讓計算機顯示諸如“hello world”的字樣可以嗎?這就要求我們實現字符加載的功能。
在開機的默認狀態下,顯示器處於80*25的文本模式。顯存的範圍是0xB8000到0xBFFFF,共計32KB。每兩個字節代表一個字符,其中低字節表示字符的ASCII碼,高字節表示字符屬性。一個屏幕總共可以顯示25行,每行80個字符。我們可以使用BIOS的顯示中斷完成顯示。
INT 10H 是由 BIOS 對屏幕及顯示器所提供的服務程序。使用 INT 10H 中斷服務程序時,先指定 AH 寄存器爲下表編號其中之一,該編號表示欲調用的功用,而其他寄存器的詳細說明,參考表後文字,當一切設定好之後再調用 INT 10H。
如果要清楚屏幕,我們可以使用:
mov ah, 06h ; 屏幕初始化或上卷
mov aL, 00h ; AH = 6, AL = 0h
mov bx, 01111h ; 設置底色爲藍色
mov cx, 0 ; 左上角開始: (0, 0)
mov dl, 4fh ; 到第x列
mov dh, 1fh ; 到第x行
int 10h ; 顯示中斷
如果要顯示一個字符,我們可以使用:
;顯示字符
mov ah, 09h
mov al, 'O'
mov cx, 1
mov bx, 0014h ; 頁號爲0(BH = 0) 藍底紅字(BL = 14h)
int 10h ; 顯示中斷
附表是它們的說明
表1-1 BIOS int 10號說明表
AH |
功 能 |
調用參數 |
返回參數 / 註釋 |
1 |
置光標類型 |
(CH)0―3 = 光標開始行 (CL)0―3 = 光標結束行 |
|
2 |
置光標位置 |
BH = 頁號 DH = 行 DL = 列 |
|
3 |
讀光標位置 |
BH = 頁號 |
CH = 光標開始行 CL = 光標結束行 DH = 行 DL = 列 |
4 |
置顯示頁 |
AL = 顯示頁號 |
|
5 |
屏幕初始化或上卷 |
|
|
6 |
屏幕初始化或上卷 |
AL = 上卷行數 AL =0全屏幕爲空白 BH = 捲入行屬性 CH = 左上角行號 CL = 左上角列號 DH = 右下角行號 DL = 右下角列號 |
|
7 |
屏幕初始化或下卷 |
AL = 下卷行數 AL = 0全屏幕爲空白 BH = 捲入行屬性 CH = 左上角行號 CL = 左上角列號 DH = 右下角行號 DL = 右下角列號 |
|
8 |
讀光標位置的屬性和字符 |
BH = 顯示頁 |
AH = 屬性 AL = 字符 |
9 |
在光標位置顯示字符及其屬性 |
BH = 顯示頁 AL = 字符 BL = 屬性 CX = 字符重複次數 |
|
A |
在光標處只顯示字符 |
BH = 顯示頁 AL = 字符 CX = 字符重複次數 |
|
E |
顯示字符(光標前移) |
AL = 字符 BL = 前景色 |
光標跟隨字符移動 |
13 |
顯示字符串 |
ES:BP = 串地址 CX = 串長度 DH, DL = 起始行列 BH = 頁號 AL = 0,BL = 屬性 串:char,char,……,char AL = 1,BL = 屬性 串:char,char,……,char AL = 2 串:char,attr,…… AL = 3 串:char,attr,…… |
光標返回起始位置 光標跟隨移動 光標返回起始位置 光標跟隨串移動 |
動手實踐
在實現一個簡單的操作系統時,我們是不可能拿一臺真正的機器做實驗的,一是很少有人有這個條件,還有就是那樣做比較麻煩。所以我們使用虛擬機來模擬一臺真實的電腦,這樣我們就能直接用虛擬機加載軟盤鏡像來啓動了。
在Windows下有很多虛擬機軟件,例如Virtual PC、Vmware和Bochs等等。其中微軟公司推出的VirtualPC2007已經提供了免費下載,同時Bochs系統調試功能非常強大,我們將這兩個平臺作爲主要的實現平臺。
Virtual PC做演示,而Bochs主要用作調試。今天的實驗我們主要使用Bochs虛擬機,下面給出一些虛擬機設置的指導。
Bochs是一種開源且高度可移植的IA-32(x86)PC模擬器,用C++寫成,能夠在大部分常見的平臺上運行。它包括了對Intel x86 CPU,通用I/O設備,和定製BIOS的模擬。通常情況下,Bochs能夠被編譯成模擬386,486或者Pentium CPU。Bochs能夠模擬運行大部分的操作系統,包括Linux,DOS 和 Windows。
Bochs的下載可以通過" https://sourceforge.net/projects/bochs/ "訪問,如圖1-2所示:
圖 1-2 Bochs下載鏈接
接下來,會導航到bochs的發行界面界面,如圖1-3所示:
圖 1-3 Bochs發行導航
最後,進入下載界面,如果是Windows平臺,選擇exe格式即可,如圖1-4所示:
圖 1-4 Windows版本的Bochs
下載完畢之後,一路next安裝即可。(安裝時建議將自帶的“DLX Linux Demo”選中,這樣可以有利於我們參考Bochs的安裝文件)
其次,我們需要下載的工具是彙編編譯器nasm。類似的,我們可以到NASM的官方網站下載,參考網址爲
下載完畢之後,一路next安裝即可。
最後,由於我們是在虛擬機上進行試驗,我們還需要一個虛擬軟盤讀寫工具,這裏我們使用“dd for windows”,下載地址爲
http://www.chrysocome.net/download/ dd-0.5.zip
直接解壓即可。
引導扇區編寫顯示字符串的引導程序
ORG 7c00h ;告訴編譯器,在7c00處加載程序
mov ax, cs
mov ds, ax
mov es, ax
; 清屏
mov ah, 06h ; 屏幕初始化或上卷
mov aL, 00h ; AH = 6, AL = 0h
mov cx, 0 ; 左上角開始: (0, 0)
mov dl, 4fh ; 到第x列
mov dh, 1fh ; 到第x行
int 10h ; 顯示中斷
; 顯示字符串
mov ax,&Msg
mov bp,ax ; es:bp=串地址
mov ah, 13h ; AH:13顯示字符串
mov al, 1h ; AH = 13, AL = 01h
mov bx, 000Bh ; 頁號爲0(BH = 0) 黑底青字(BL = 0Bh)
mov cx, MsgLngth ; CX = 串長度
mov dl, 00h ; 起始列
mov dh, 00h ; 起始行
int 10h ; 顯示中斷
Msg: db "Welcome To OS World ^-^";
首先,我們將上述代碼使用記事本編寫,並保存爲boot.asm。接下來我們使用nasm對上述代碼進行編譯:
在開始菜單中,選中運行,在運行中鍵入cmd,打開命令行菜單。
圖1-5進入命令行模式
在命令行狀態下,切換到nasm的安裝目錄,在我的機器中nasm的安裝路徑爲D:\Program_Files\nasm,我們使用如下的dos命令:
cd D:\Program_Files\nasm,如圖1-6所示:
(提示安裝路徑中最好不要有空格或中文)
圖1-6 切換工作目錄演示
此時,我們的工作目錄仍然是邏輯盤符C,我們鍵入D:,即可到達nasm目錄。
圖 1-7 進入nasm目錄
這是因爲我們需要啓動nasm才能將我們boot.asm代碼編譯成爲可執行代碼,我們將我們剛剛編寫好的boot.asm,拷貝到nasm目錄下,鍵入如下命令:
nasm boot.asm –o boot.bin
在虛擬機中,我們是使用虛擬文件vhd來模擬硬盤的,我們可以直接在windows 10中使用diskpart命令完成,首先我們進入cmd命令行,如圖1-8所示:
圖1-8 使用diskpart生成虛擬硬盤
在接下來,我們在diskpart中輸入命令:create vdisk file=你需要存放的文件名(我這裏是f盤下的dingst.vhd)maximum=10 type=fixed,如同1-9 所示
圖 1-9獲得vhd文件
接下來,我們將得到dingst.vhd文件拷貝到“dd for windows”的安裝目錄。同樣的,我們將編譯成功得到的boot.bin拷貝至“dd for windows”的安裝目錄,在命令行狀態下切換到該目錄,鍵入如下命令:
dd if=boot.bin of=dingst.vhd bs=512 count=1
就將我們的可執行文件拷貝至a.img虛擬軟盤中,我們啓動Virtual PC。在首次進入Virtual box時,我們需要生成一個虛擬機:
首先,系統會啓動“新建虛擬機嚮導(New Virtual Machine Wizard)”,詢問是否使用嚮導一步步創建新的虛擬機。我們選擇“Create a virtual machine”,點擊“Next”,如圖1-9所示:
圖 1-10 設置Virtual box虛擬機
接下來系統會提問將虛擬機文件存放在哪個位置,我們選擇虛擬機的存放路徑。然後,類型選擇other,如圖1-10所示,其他均保持默認即可:
圖1-11 設置虛擬機類型
同時,內存選擇16MB爲宜,最大不要超過64M(當年windows95,超過64MB的內存,OS反而會崩潰)。
圖1-12 設置虛擬機內存
再接下來,核心是選擇已經燒錄了引導程序的vhd作爲你的硬盤,如圖1-13所示:
當虛擬設置完畢,我們點擊start按鈕,啓動虛擬機,如圖1-11所示:
圖1-13選擇虛擬機的硬盤
接下來直接啓動我們的虛擬機,即可得到我們的實驗結果,如圖1-14所示:
圖1-14 虛擬硬盤引導啓動成功