電腦從開機加電到操作系統main函數之前執行的過程

總的來說在操作系統加電啓動之後到main函數執行之前操作系統經歷了以下3個大步驟
1.啓動BIOS。這個時候位於實模式下,加載中斷向量和中斷服務程序
2.加載操作系統內核併爲保護模式做準備。這個時候操作系統一共加載了3部分代碼:引導程序bootsect,內核代碼setup,內核代碼system模塊
3.從實模式轉換爲32位保護模式。這個過程要做大量重建工作,並且持續工作到操作系統main函數的執行過程。細說的話,主要包括打開32位尋址空間,打開保護模式,建立保護模式下的中斷相應機制與保護模式配套的相關工作,建立內存分頁機制。

名詞說明:
實模式:20位的存儲器地址空間,可以直接軟件訪問BIOS及周邊硬件,沒有硬件支持的分頁機制和實時多任務概念。從80286開始,所有的80x86CPU開機狀態都是實模式。
中斷向量表:實模式下用於記錄所有中斷號對應的中斷服務程序的內存地址。
中斷服務程序:用於指示中斷髮生後該怎麼辦的程序。
SS:棧基址寄存器,SP:棧頂指針,兩者一起構成棧在內存中的位置。壓棧方式爲高地址向地地址。
根文件系統設備:linux 0.11使用Minux OS的文件系統管理方式,要求系統必須存在一個根文件,其他文件系統掛在其上。因此Linux 0.11在啓動時需要兩部分數據,即系統內核鏡像和根文件系統。
GDT(全局描述符表):在系統中唯一的存放段寄存器內容的數組,配合程序進行保護模式下的段尋址。其可理解爲所有進程的總目錄表,其中存放每一個task局部描述符表(LDT)地址和任務狀態段(TSS)地址,完成進程中各段的尋址,現場保護與現場恢復。
IDT(中段描述符表):保存保護模式下所有中斷服務程序的入口地址,類似於實模式下的中斷向量表。
GDTR,IDTR:分別爲以上描述符表的基地址寄存器
CR0寄存器:0號32爲控制寄存器,存放系統控制標誌。第0位爲PE標誌,置1時CPU工作在保護模式下,置0時爲實模式。
設置段寄存器指令:該組指令的功能是把內存單元的一個“低字”傳送給指令的指定16寄存器,然後把“高字”傳給相應的段寄存器。命令格式:LDS/LES/LFS/LGS/LSS Mem, Reg

說明:我學的是linux 0.11內核,目前有的內核版本是3.6,雖然差好多,但是適合學習。
注意:開始階段的BIOS與操作系統無關


1.啓動BIOS,準備實模式下的中斷向量表和中斷服務程序
電腦啓動後,CPU邏輯電路被設計爲只能運行內存中的程序,沒有能力直接運行存在於軟盤或硬盤中的操作系統,如果想要運行,必須要加載到內存(RAM)中。
BIOS是如何啓動的:
CPU硬件邏輯設計爲在加電瞬間強行將CS值置爲0XF000,IP爲0XFFF0,這樣CS:IP就指向0XFFFF0這個位置,這個位置正是BIOS程序的入口地址。
BIOS在內存中加載中斷向量表和中斷服務程序
    BIOS程序被固化在計算機主機板上的一塊很小的ROM芯片裏。現在CS:IP已經指向了0XFFFF0這個位置,意味着BIOS開始啓動。隨着BIOS程序的執行,屏幕上會顯示顯卡的信息,內存的信息……說明BIOS程序在檢測顯卡,內存……期間,有一項對啓動操作系統至關重要的工作,那就是BIOS在內存中建立中斷向量表和中斷服務程序。
    BIOS程序在內存最開始的位置(0x00000)用1KB的內存空間(0x00000~0x003FF)構建中斷向量表,在緊挨着它的位置用256KB的內存空間構建BIOS數據區(0x00400~0x004FF),並在大約57KB以後得位置(0x0e05b)加載了8KB左右的與中斷向量表相應的若干中斷服務程序。
    中斷向量表有256箇中斷向量,每個中斷向量佔4個字節,其中兩個字節是CS值,兩個字節是IP值。每個中斷向量都指向一個具體的中斷服務程序。

2.加載操作系統內核併爲保護模式做準備
現在開始計算機要分三批逐次加載OS的內核代碼。分別是引導程序bootsect,內核代碼setup,內核代碼system模塊
加載引導程序bootsect
    首先對CPU發送int 0x19中斷,使CPU運行int 0x19中斷對應的中斷服務程序,這個中斷服務程序的作用就是把軟盤第一個扇區的程序加載到內存的指定位置。總結來說就是:找到軟盤並加載第一扇區。這個第一扇區的內容就是bootsect,第一扇區稱爲啓動扇區。
    至此,第一批代碼bootsect已經加載進內存了,下面的工作就是執行bootsect把軟盤的第二,三批代碼加載入內存。
加載第二部分程序setup
    bootsect首先對內存進行規劃:包括之後將要加載的setup程序的扇區數,被加載到的位置,啓動扇區被BIOS加載的位置等等。總之就是規劃好之後內存分佈,方便之後使用。
    bootsect接着複製自身代碼。從內存0x07c00複製到內存0x90000處。這個時候說明OS開始根據自身的需求安排內存了。
    最後,bootsect將setup加載至內存。其中加載過程需要藉助BIOS提供的int 0x13中斷向量指向的中斷服務程序來完成。該程序將軟盤第二個扇區開始的4個扇區,即setup.s對應的程序加載至內存的SETUPSEG(0x90200)處。
加載第三部內核代碼--system模塊
    加載第三批代碼仍然用int 0x13中斷向量。
    這次加載的扇區數是240個,爲了讓用戶覺得不是機器故障,Linus在屏幕上設計了打出一行字符串:"Loading system ... "
    加載完畢後再確定一下根設備號。

3.從實模式轉換爲32位保護模式
    至此,3部分代碼已經加載完畢,開始轉變進入保護模式。
關中斷並將system移動到內存地址起始位置0x00000
    關中斷是爲了不響應外部中斷,直到保護模式下的中斷服務體系被構建完畢纔會打開中斷。
    回顧一下,0x00000這個位置原來存放着由BIOS建立的中斷向量表級BIOS數據區。這個複製會覆蓋原區域。這樣的好處是:
        a)廢除BIOS的中斷向量表。
        b)內存空間回收
        c)讓內核代碼佔據內存物理地址最開始的有利位置
設置中斷描述附表和全局描述符表
    設置並初始化這兩個表
打開A20,實現32位尋址
    A20爲第21根地址線,實模式下啓用前20根(A0~A19),保護模式將開啓A20~A31,標誌32位尋址。
爲保護模式下執行head.s做準備
    爲了建立保護模式下的中斷機制,setup程序將對可編程中斷控制器8259A進行重新編程,重新映射中斷號。
    接着執行代碼:
    ...
    mov ax,#0x0001   ! protected mode bit
    lmsw ax
    jmpi 0,8                 ! jmp offset 0 of segment 8 (cs)
    ...
    其中8代表1000,最後兩位表示內核特權級,11則表示用戶權級;第三位0表示GDT,1則表示LDT;1表示選擇表中的第1項(從0開始),這個用來確定爲代碼段(cs)的段基址和段限長等信息。示意圖如下:
    


head.s開始執行
    head.s的加載方式與之前的bootsect,setup有所不同,大致過程是:先將head.s彙編成目標代碼,將用C語言編寫的內核程序編譯成目標代碼,然後鏈接成system模塊。也就是在內存既有內核程序,又有head程序。兩者是緊挨着的。
    
     head程序除了做一些調用main之外的工作之外,還主要用程序的自身代碼在程序自身所在的內存空間創建了內存分頁機制。
    head程序將L6標號和main函數入口地址壓棧,棧頂爲main函數地址,目的是使head程序執行完後通過ret指令就可以直接執行main函數。正常來說main函數是不應該退出的,但如果main函數異常退出,就會返回這裏標號L6處繼續執行,防止系統崩潰。
    這些壓棧操作完成後,head執行setup_paging:去執行,開始創建分頁機制。先要將頁目錄表和4個頁表放在物理內存的起始位置。
    4個頁表分別映射0x0000到0xFFF000的所有頁面,一個頁面大小爲4KB
    
    head程序設置完的內存分佈示意圖
 

至此,開機到main函數執行之前的過程結束。


參考:Linux內核設計的藝術

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