1. 程式人生 > >閱讀linux0.11——boot

閱讀linux0.11——boot

本文主要圍繞三個問題展開:
1、為什麼要有載入程式?
2、什麼是載入程式?
3、Linux 0.11載入程式怎麼工作?

一、為什麼要有載入程式?

解釋這個問題以前,先說一點BIOS的工作。當你按下電源鍵以後,計算機先進行的工作是加電自檢,自檢完了然後就到了BIOS大展身手了,它負責“點醒”硬體,然後把載入程式BIOS也不知道自己把什麼東西載入進記憶體。這裡要注意BIOS載入的位置:把硬碟上的第一個扇區載入進記憶體的0x7c00位置。

所以第一個問題就來了,我們知道一個扇區只有512位元組,假設我們沒有載入程式,BIOS把系統載入進記憶體,那就意味著你的系統程式碼只有512位元組。你沒有看錯是512位元組,這其中還包括了真實模式的跳轉程式碼和你寫的註釋。

如果你說,我geek一點,我寫的系統就是隻有512位元組。那你 本文對你來說沒有任何需要閱讀的意義。

所以為了突破這個512位元組的限制,我們設計出了載入程式。這就引入了第二個問題,什麼是載入程式。

二、什麼是載入程式

載入程式顧名思義就是負責把系統引導進記憶體的程式,剛剛已經解釋了這個的重要性。現在來看看載入程式到底實際上需要做什麼。

第一點也是最重要的一點,把系統load進記憶體一個特定的位置。其次,剛開機的時候是不是還是在是模式下,如果可能的話我們希望引導也能幫我跳轉到保護模式。我希望我的系統第一個模組就是在32位系統下執行的。

捋一下就是主要有兩件事:
1、載入系統
2、跳轉到保護模式

是的,就是這麼簡單,我們下來看看具體的實現。

三、Linux 0.11載入程式怎麼工作

Linux0.11boot主要分為三個部分,分別在三個程式裡面實現,bootsect.s、head.s和setup.s。這個三個程式執行的順序是:bootsect–>setup–>head。

bootsec:把自己從0x7c00位置移動到0x90000位置,載入setup程式把系統載入在0x10000位置,
setup:把系統搬移到0x00000位置,讀取一個磁碟、顯示或者記憶體的引數並儲存在合適的位置,然後跳轉到保護模式(保護模式跳轉的詳細分析見博文http://blog.csdn.net/sium__/article/details/50082285

1、bootsect.s

這個程式是最先被執行的,根據Linus的解釋說,這個程式會被BIOS啟動例程(bios-startup routines)載入到記憶體的0x7c00處(這個地址是BIOS約定好的,每一個系統的引導都會被從硬碟的第一個扇區讀入這個位置。這也就一位這,每一個引導都要在第一個扇區包括自己寫的)。但是這裡請大家注意,這個地址的表示方式是16位的,表示現在還是在真實模式下。然後bootsect.s程式會把自己移動到記憶體的0x90000處,這個地址是20位的地址了。最後jump到0x90000處。程式載入setup程式到記憶體的0x90200處,並且此時系統在記憶體的0x10000處。

下面是程式的詳細解釋:

<此時在真實模式下>
把0x7c00寫入ds暫存器
把0x90000寫入es暫存器
源變址暫存器和目的變址暫存器清零
迴圈執行當前程式碼移動到記憶體的0x90000
jmp到0x90000處程式碼的go標籤處,這裡提一點,jmp指令執行完了以後,CPU會自動修改cs暫存器的內容
把cs暫存器的內容寫入ds、es暫存器
定義一個棧,空間大小是0xfeff,當前的棧頂地址是0x9ff00
利用中斷int 0x13把磁軌號0,扇區號2磁頭號0、驅動號0的內容讀入記憶體的0x90200處,讀四個扇區
**如果讀失敗**
重置驅動號重試一次
**如果讀成功:**
通過BIOS中斷int 0x13 獲取磁碟引數(包括磁軌號、扇區數、驅動器號、和需要讀出的磁頭號)
儲存磁軌號和扇區
重新將0x9000寫入es
呼叫中斷0x10(功能號:0x03)讀取游標的位置和形狀
呼叫中斷0x10(功能號:0x13)輸出24個字元(loading system...)
把0x1000寫入es暫存器
呼叫度磁碟過程
呼叫關閉軟碟機過程

這裡說一點題外話,彙編也是有類似於別的語言的函式,我們稱之為過程,過程的呼叫原理類似於c語言的函式呼叫,呼叫完了還是要回到過程被呼叫的地方來。為了使大家對程式也有更清晰的認識,我們按照程式執行的順序對程式分層,按照層次講解。因此這兩個過程解析見後文。

取出根裝置,
如果根裝置號不等於0,跳轉至root_defined處
取出扇區數如果扇區數是15,則說明是1.2Mb的驅動器;如果扇區數是18,則說明是1.44Mb的軟碟機。
根據不同的類別儲存不同的根裝置號
跳轉到記憶體的0x90200處,執行setup程式

到這裡程式的主要部分就結束了,在分析read_it過程之前,先來捋一下。我們可以根據以上的分析得出如下結論:
1、bootsect程式執行的主要工作是把自己從記憶體的0x7c00處移山填海到0x90000處,把setup程式搬移到0x90200處。實際上還有一個很重要的工作,就是把存在磁碟上的系統讀入記憶體的0x10000處
2、根據這個記憶體的安排我們可以大膽的認為:bootsect程式沒有0x0200位元組長(請大家注意這一點)。

接下來我們分析過程read_it:
判斷es是不是在64KB的邊界上,如果不是就死迴圈
根據剛才讀出來的磁碟引數(磁頭號、驅動號、磁軌號、扇區號)判斷是不是已經讀完、是不是要換一個磁軌和是不是要換一個扇區,然後呼叫BIOS中斷觸發讀磁碟操作

這裡boot程式執行完了,並把控制權交給了setup程式。

2、setup程式

前面已經大致解釋了setup程式完成的功能:呼叫BIOS中斷獲取一些系統的引數,依次把它們順序儲存在0x90000位置(覆蓋了bootsect程式),然後把系統搬移到0x00000位置,最後跳轉至保護模式,把控制權交給head程式。語言看起來清晰明朗,這裡不在贅述。

值得一提的是,bootsect程式和setup程式都算是載入程式,為什麼把它們寫了兩個程式,這個原因跟為什麼要引入載入程式一樣,因為512位元組寫不下。

head是系統的第一個模組,執行在保護模式下。