I386體系結構(下)

 

I386體系結構(下)

摘要:上半期我們一起學習了I386體系結構,下半期我們的主要目標是實現一個能啓動而且可以進入保護模式的簡易操作系統。所以本期首先來分析一下計算機的啓動流程,然後着手學習開發一個基於I386體系的可啓動系統。

Linux啓動流程

我們先來分析系統的啓動流程吧!

硬件準備

計算機加電的瞬間,整個系統包括RAM在內的幾乎所有部件,都處於一種隨機的混亂狀態,所以根本談不上做什麼實際工作。因此,啓動機器首先得將系統帶出這種黑暗和混亂的狀態。

硬件在這個時候必須責無旁貸,挺身而出。啓動一開始,硬件系統(是主板吧!)就會給CPUReset的管腳發送一個信號,促使CPU將自己的主要寄存器(包括cseip)設置爲啓動狀態──實際上就是把這些寄存器的內容設置成一些預定值(cs=F00h,eip=FFF0h)。我們都知道指令寄存器(eip)決定系統下一步要執行什麼,所以指令寄存器被預置成一個固定值,那麼該固定位置上的指令就會被執行——在這裏eip指向了位於物理地址0xFFFFFFF0上的指令,所以該指令便作爲系統加電後執行第一條指令了。

硬件系統通常會把一個只讀芯片的存儲區映射到這個位置上(0xFFFFFFF0)──這種芯片一般來說就是ROM。所以ROM中存放的程序就會被調用執行——換句話說就是機器加電後首先執行ROM中存放的程序,傳統上稱該程序爲BIOS(Basic Input/Output System)

BIOS的作用

按照啓動流程,該談談BIOS了。

     BIOS是固化在機器ROM芯片中的一小片程序,在機器啓動後會首先執行這片代碼。那麼這段代碼到底是做什麼的呢?爲何要首先執行它呢?

要回答這個問題,就要想想計算機的啓動過程。也許你不加思索的回答說,啓動就是計算機從磁盤裏調入操作系統,然後開始運行它。豪無疑問,你回答是正確的,但是你忽略了一點──機器發現磁盤,找到操作系統在磁盤中位置這個過程。顯然這個過程不能藉助於磁盤中的程序來完成,因爲還沒找到磁盤啦,所以機器自己必須留一手用本身“自帶”的程序來找磁盤,讀磁盤數據。這一手就是BISO中的程序片。

別小看這小片程序,它可是關係系統能否正常啓動的關鍵數據呀所以必須保護它不會被篡改,這也正式爲什麼要它固化在 ROM中的原因。一般情況下BIOS中的數據不會被破壞,但也由些特例,比如陳英豪搞的CIH病毒,這個可惡的病毒會破壞BISO數據(只能破壞那些可寫入的幾款BISO),造成的系統無法啓動。

         具體的講,BISO 中的程序包含POST(加電自檢)、開機菜單設置、裝載引導扇區和 BISO中斷等幾部分程序片

l        POST會在開機後首先運行,去檢測內存大小初試化硬件設備(對於基於PCI總線的體系結構,這個過程很關鍵,因爲只能通過這個過程來保證所有的硬件設備不會因爲中斷或I/O端口等資源發生衝突。這個過程完成時,會在屏幕上顯示系統中PCI設備的清單

l        接着啓動菜單(setup menu)被執行。啓動菜單程序爲提供設置系統參數(如時鐘,啓動設備)的配置界面(多數情況下,用戶開機時按下”DEL”鍵盤,會進入配置界面)

l        再下來就時從啓動設備中裝載引導扇區(0個扇區),即加載第0個扇區,載入位置固定在0000:7C00處(這是個默認的規定)

l        ROM BISO中斷程序則是用來控制屏幕、鍵盤、磁盤驅動、串口等簡單驅動設備的。引導扇區載入就會很頻繁的調用這些中斷來讀取磁盤扇區數據  (這些中斷在系統啓動後,不一定再繼續被使用,比如Linux系統中就會重寫這些中斷程序,不過也有系統仍然使用BISO中斷,比如DOS系統)

 

    總結一下,BISO首先會執行自檢程序,然後可按用戶需要進入啓動菜單進行系統配置;如果不需要配置則去啓動設備載入引導扇區。到此BISO的任務完成了,剩下的啓動任務都交接給引導扇區中程序去繼續完成吧。總而言之,BISO要做的就是找到啓動設備(啓動分區所在設備),然後從中讀取第0個扇區(啓動扇區了)OK了。

   

現在問題到了啓動扇區了。啓動扇區安排在磁盤(軟盤或硬盤)的第一個扇區,大小爲512個字節,它的標誌是末尾兩位是0x550xAA——BISO當檢測到這兩個字節時,就會跳轉到0000:7C00處執行(如果沒檢測到,自然不跳了)。

 

0000:7C00地址開始,啓動任務交給了啓動裝載(boot loader)程序繼續完成,該程序負責將操作系統內核載入系統並完成必須的初始化工作,再把執行權交給操作系統運行。

啓動程序包含兩部分內容:

l    啓動扇區程序(boot sector program)

存於啓動設備第0個扇區中的代碼被稱爲啓動扇區程序,它佔有512個字節,最後兩個字爲0x550xAA512字節內未用部分填充0。這片程序很小,所以功能只限於拋磚引玉——加載第下一階段程序(通常情況,不絕對! 有時會加載另一個啓動扇區程序,比如MBR裏的引導扇區;有時可以直接載入緊接着啓動扇區的內核,即位於第1個扇區的內核)

l         第二階段程序[1](second stage program)

第二階段程就是上面所提到的下一階段程序,它一般緊跟着啓動扇區程序存放(編譯時制定)在磁盤。當它被載入內存後,接着就會被執行。第二階段程序主要負責——加載內核到內存——然後——進行必要的初始化工作,爲內核運行做準備。具體要作的工作主要是爲進入保護模式作初始化工作。

進入保護模式

到了談談進入保護模式的時候了!

保護模式並不是你想進就能進入的在進入之前你必須作些準備:

1.               建立一個可用的全局描述符號表GDT,這個表裏最好能填入系統所要使用的代碼和數據段描述符號。

2.             關閉中斷別在進保護模式時候被外界打斷。

3.           將GDT裝入GDTR寄存器(原因自己去看書吧)。

4.           設置機器狀態寄存器MSW PE位,表示開啓保護模式。

5.             將數據段和堆棧段選擇子裝入到DS段寄存器中。

6.             進行一個遠(裝入CS IP/EIP) 來將32位的代碼選擇子裝入CS,至此進入保護模式。

 

          進行跳轉是爲了清楚芯片上的預取指令及譯碼序列,因爲PE置位後,機器從實地址方式轉入了保護方式,而預取及譯碼的指令不再有效,爲了避免機器錯誤尋址,必須進行一側段間跳轉指令,來廢棄預取的譯碼序列。

到此爲止,內核運行環境才被真正建立起來了,內核主程序要作的事情剩下初始化頁表(啓動分頁機制,映射內存)、初始化系統中斷/異常表等(建造IDT,設置中斷、異常入口)、初始化設備(註冊設備驅動程序,初始化外設)、啓動網絡、文件系統等等,總之是爲運行應用程序繼續做準備工作(有關內核啓動後的工作可參見第一期的第四部分中初始化基本的操作環境一節)

簡易操作系統代碼實例

好了多說無益,我們一定要把上面概念反映倒代碼裏來理解!下面我們就來看看怎樣開發一個可啓動的操作系統吧!

 

我們參照Linux系統的代碼組織結構來開發一個小小的操作系統(準確地說它不是操作系統,它僅僅走了一個簡易的系統啓動流程),它能帶你進入保護模式,完成一些系統需要的初始化工作然後能給你個啓動後通常能見到的假界面——因爲該界面還不能和你交互,所以說是個假界面(沒有實現SHELL等功能)——我們希望通過該系統能讓大家實地感受一下系統啓動過程,也希望通過這個過程能讓大家認識操作系統的原始開發過程。

Linux一樣我們將是試驗系統(就叫它SagaLinux吧)的啓動部分代碼分爲boot.s(引導扇區中的彙編代碼[2],負責載入setup.s到內存——就是啓動扇區程序)setup.s(負責繼續載入內核,並進行一定初始化和進入保護模式——就時第二階段程序兩部分,而且將它們置於boot目錄下;也和linux一樣,我們將內核代碼仍叫作kernel.c,並將它置於目錄kernel,它的功能弱的只能用來演示流程,而沒任何實際用途——它建立了4個頁表,映射16M空間;初始化了中斷表;註冊了鍵盤中斷,使得可相應鍵盤命令;然後清平幕(洗了臉來等你看它,呵呵)

 

代碼在這裏不詳細分析了,這裏僅僅給出它的執行過程圖     

             

建立頁表並初始化

建立IDT表並初始化

安裝鍵盤驅動程序

清屏

Kernel.c

               kernel.c流程圖

 

   

   就到這吧,有空去看看試驗代碼。結合上面的講解,好好琢磨琢磨源代碼,我相信你一定能領會操作系統啓動過程和I386體系的特點。

  要想試試這個系統,就去找個軟盤插入軟驅,然後執行make ; make bootdisc就會在軟盤中寫入系統了。去試試用該軟盤啓動系統吧,看看會有什麼結果。

     感謝Faik Yalcin Uygur !我們的啓動試驗代碼就是借用他的系統源代碼——我僅僅修改了名字。



[1]對於可動態配置的啓動程序,比如lilogrub,在加載內核前還包含一個用戶接口程序,提供用戶選擇啓動選項的能力。

 

[2] 因爲啓動扇區被裝載到內存時,CPU是處於實模式的,因此程序規模等受到極大限制,訪問空間被限制在1MB之內。但是GCC並不關程序是在實模式和在保護模式,用它編譯出來的程序很難在實模式下運行,因此多數代碼都使用匯編代碼完成

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章