揭開鋼琴的蓋子:操做系統比如一個架美麗的鋼琴,咱們能夠用上面的琴鍵彈出優美的旋律。可是咱們不能知足於只會彈奏,若是咱們要更深刻理解鋼琴,必須打開鋼琴的蓋子,一探究竟。因此學習操做系統,不能停留上系統API的調用,須要能更好更高效的調用API,知道API的侷限性與缺點,就必須打開操做系統的蓋子,探究操做系統API下的底層原理。html
從咱們按下電源鍵使得計算機通電,計算機的各個部件是怎麼運行起來的呢。咱們如今使用的計算都遵循馮諾依曼結構,在咱們探討計算機的啓動前,先弄明白咱們的計算機的結構。ios
1. 馮諾依曼結構計算機的工做原理
- 計算機的核心工做部件是CPU,CPU內部從上電開始反覆執行着兩個動做:1)取址;2)執行
- 計算機根據一系列的操做指令來執行不一樣的動做,這些指令就是計算機程序。
- 計算機運行的程序是以二進制的方式存在內存中,程序中的數據與指令不加區別的都存儲在內存上。
2. 計算機的啓動過程:
圖1:從系統加電起所執行的程序
- X86 PC歲開機時,CPU處於實模式,這時候內存的計算方式是
段基址 << 4 + 段內偏移
- CPU的第一條指令是經過
CS:IP
來取得,而此時CS=0xFFFF,IP=0x0000。這是硬件設定好的。
- 因此最開始執行的指令地址就是0xFFFF0,這個內存地址映射在主板的BIOS ROM(只讀存儲區)中。
- ROM中的程序會檢測RAM、鍵盤、顯示器、軟硬磁盤是否正確工做。同時會從地址0開始設置BIOS的中斷向量表。
- ROM中的程序繼續執行,將啓動設備磁盤0磁道0扇區,一個512字節的扇區讀到內存0x07c00處。0x07c00應試是一個歷史遺留的問題,後續把system模塊拷貝到地址開始處時,預留的空間將不夠,因此bootset須要把0x07c00這一塊操做系統引導與設置模塊拷貝走。這算是一個歷史包袱。
- 設置cs=0x07c0,ip=0x0000。
- ROM中的程序執行結束,轉到0x07c00處開始執行。
啓動設備是能夠經過BIOS程序來設置的,信息寫在CMOS中。CMOS(64B-128B)中存的還有實時鐘,硬件配置信息等。(開始時按住Del鍵能夠進入啓動設置的配置界面,能夠設置光盤啓動或U盤啓動等)。函數
圖2:內核在磁盤上的分佈狀況
3. Bootsect.s作了哪些事
- 把0x7c00開始的512個字節,拷貝到0x90000處。(0x90000 - 0x90020)
- 設置棧ss = 0x9000,sp = 0xff00,這裏把sp設置的夠大,防止棧的區域把下面的操做系統代碼覆蓋了。
- 調用BIOS ox13中斷,將第2-5個扇區拷貝到0x90020開始的內存處。若是出錯,就反覆讀取。
- 獲取磁盤的參數:磁道數等
- 打印字符串信息:system is loading
- 讀入system部分(幾百個扇區),讀入到內存爲0x10000處。(在0x90000的下面)
- 轉到地址爲0x90020的地址處執行,也就是開始執行setup部分的代碼了。
4. Steup模塊作了什麼事
主要工做是完成操做系統啓動前的設置工做。學習
- 讀取光標的位置信息放在09000的頭2個字節處。由於這時候bootsect模塊的代碼已經沒有用了,能夠覆蓋了。
- 讀出擴展內存的大小,放在接着的2個字節處。
- 獲取顯卡參數,硬盤參數等等。
- 將system模塊的內容從0x10000處開始移到0x00000處,即內存的起始位置。之因此Load進來的時候爲何不一次性放在0x00000處,是由於0x00000處開始放的bios中斷。如今bios中斷已經不須要了,因此能夠覆蓋了。
- 這時候開始,BIOS的中斷向量表已經被覆蓋了,後面就再也不須要BIOS的中斷了。
- 設置中斷向量表與全局描述符表的一部份內容。
- 把cr0的最後一位設置爲1,也就是說從實模型進入保護模式。
jmpi 0, 8
。 cs = 8,取到的段基址實際上是0x0000,那麼這句話就是跳轉到地址爲0x00000的地方開始執行,也就是system模塊的開始部分。
保護模式下地址翻譯與中斷處理的改變:測試
cp:ip的翻譯過程是:從cs的前12位取出GDT的偏移量(這裏是1),從gtd的對應表項中取得基地址,再和ip合併爲一個完整的地址。
int n: n指明瞭IDT表中的序號。從IDT表中獲取中斷處理函數的入口地址。操作系統
5. system-head
System的第一部分就是head.s部分的代碼,這部分代碼實際處於絕對地址0處開始的地方。該部分的代碼是在保護模式下執行的,所使用的是AT&T格式的彙編指令與以前使用的as86彙編指令不一樣。這部分的代碼主要完成了下面幾件事情。翻譯
- 初步始中斷描述符中的256項門描述符。
- 檢查A20地址線是否打開。關於A20地址線的解釋
- 測試系統是否含有數據協處理器,並設置寄存器CR0對應的位。
- 初始化內存頁目錄表,爲內存分頁管理做好準備工做。頁目錄表放在了絕對物理地址爲0開始處,也就是head.s程序物理內存位置,程序會被覆蓋掉。80286當時24根地址線,尋址16M,因此頁表要能尋址16MB。若是內存頁大小爲4k,那頁表就有4K個表項,一個表項按4個字節算,那頁表就須要16K個字節(4頁)。這裏只用到了1級頁表,在後續的發展中出現了二級頁表,3級頁表。
- 最後跳轉到system模塊中的初始化程序init/main.c中繼續執行。
head.s程序執行結束後,已經正式完成了內存頁目錄頁表的設置,並從新設置了內核實際使用的中斷描述符表idt和全局描述符表gtd。另外還爲軟盤驅動開闢了1kb的緩衝區。此時system模塊在內存中的詳細映像以下圖所示:code
圖3:System內存中的映像示意圖
6. 整體執行線路
總體上能夠分類6個階段,頭2個階段爲boostset,中間3個階段爲setup,最後一個階段爲system的head模塊。htm
圖4:啓動引導的整個過程當中,內核在內存中的位置以及移動後的位置狀況
7. 參考資料
[1] 《Linux內核徹底剖析基於0.12內核》 趙炯著。
[2] 網易雲課堂,哈爾濱工業大學《操做系統之應用》 李治軍。blog