Bootloader:加載OS。操作系統一開始是放在DISK(硬盤)中,并不是放在內存中。
BIOS:基本I/O處理系統。存放在ROMRead-Only Memory)只讀存儲中
?
BIOS(Basic Input/Output System)基本輸入輸出系統。
首先我們需要知道,計算機加電之后,是從什么地方讀取的第一條指令,從磁盤的什么地方讀取的我的操作系統的內容,也就是計算機啟動的過程是怎樣的。
計算機加電后,首先是CPU的初始化過程,將各寄存器初始化,此時系統處于實模式下。我們要知道計算機內存分為兩種,RAM(Random Access Memory)隨機訪問存儲和ROM(Read-Only Memory)只讀存儲。其中RAM掉電內容會丟失(也就是我平時所理解的內存條內的內容比如),ROM掉電不會丟失內容。因此系統初始化代碼就存儲在ROM中。由于CPU初始化完成后處于實模式,尋址空間為20位也就是1M,因此系統初始化代碼也就是BIOS啟動固件是存儲在1M之下,如下圖。還需要知道的是,CPU初始化完后CS代碼段寄存器與IP指令指針寄存器決定了第一條要執行的代碼在ROM中的位置,而BIOS能提供的功能也列在下圖之中:
- 基本輸入輸出的程序(從磁盤讀數據、從鍵盤讀輸入、顯示器上顯示輸出等);
- 系統設置信息(從硬盤啟動還是從網絡啟動還是從U盤啟動等);
- 開機后自檢程序(檢查內存顯卡是否正常);
- 系統自啟動程序(啟動操作系統)等。
BIOS會根據設置執行啟動程序,從磁盤把加載程序和操作系統內容加載到系統。首先BIOS將磁盤讀引導扇區加載進指定位置0x7c00,引導扇區大小限制為512byte。此時跳轉到CS:IP = 0000:7c00執行引導扇區的程序段,通過引導扇區的程序段跳轉到讀取操作系統的加載程序。加載程序主要有兩個功能:
- 將操作系統的代碼和數據從硬盤加載到內容中;
- 將控制權轉移給操作系統,也就是CS:IP跳轉到操作系統代碼段,執行操作系統的功能。
那么問題來了,既然能讀磁盤內容,為什么不直接將操作系統內核映像讀取到內存空間而要先讀取引導扇區的加載程序呢?這是因為不同的操作系統有不同的文件系統,也就需要不同的啟動方式。而加載程序限制為512字節,無法覆蓋所有類型的系統,因此首先將磁盤引導扇區內的程序加載進來,引導扇區內的程序可以識別并加載不同類型的系統內核到內存中(亦或是不同的系統有著不同的引導扇區,加載進來之后就知道是什么類型的系統了,至少學到現在還不知道,后續可能會有詳細介紹吧)。此時控制權轉移給操作系統內核。
除此之外BIOS還可以使用一些簡單地系統調用,比如說:1. INT 10h:字符顯示;2. INT 13h:磁盤扇區讀寫;3. INT 15h:檢測內存大小;4. INT 16h:鍵盤輸入。這些系統調用都只是最簡單的系統調用,并且只能在實模式下訪問。當進入操作系統并且操作系統是保護模式,那么這些功能就不能用了。
系統啟動流程
上一節講解系統調用的過程中粗略的講了一下系統啟動的流程,然而實際上系統地啟動流程遠沒有那么簡單,本節就稍微詳細地講解一下。但是實際上即使是這一節也無法十分詳細講解,只是讓我們對系統啟動的過程有一個整體的認識而已。
首先是BIOS讀取加載程序的過程:系統加電后,CPU初始化,然后BIOS初始化硬件,然后查詢主引導記錄讀取主引導扇區代碼,這是由于現代計算機內通常不止有一個分區,不同分區內可能有不同的操作系統,而我們需要知道我們要啟動的操作系統在哪個分區內(稱為活動分區),這就是通過讀取主引導記錄實現的。知道活動分區之后將活動分區引導扇區內的代碼讀取到內存并執行,引導扇區內的代碼讀取文件系統的加載程序。這才是較為完整的讀取加載程序的過程。
那么再詳細一點講,計算機上電后,首先是CPU初始化:CPU上電后,通過CS、IP兩個寄存器知道從0XFFFF0地址讀取第一條指令,這條指令是跳轉指令,跳轉到BIOS程序段。為什么是0XFFFF0呢,這是因為CPU上電后會初始化CS:IP = 0xf000:fff0。并且CPU初始化后處于實模式,也就是16位系統,20位尋址空間,是通過將CS左移4位與IP相加得到最終的地址因此指令指針PC = 16*CS+IP。20位尋址空間自然意味著最大尋址空間為2^20byte,即1M。
接下來是BIOS初始化:硬件自檢(ROM等);檢測內存(RAM)、顯卡等關鍵部件是否存在,存在的話工作狀態是否正常;查找并執行顯卡等接口卡的BIOS(顯卡等擁有自己的BIOS固件),對設備進行初始化;之后執行系統BIOS進行系統檢測,主要是為了檢測計算機有哪些即插即用設備,比如硬盤、光驅或者U盤;檢查完之后更新CMOS中的擴展系統配置數據(ESCD)告知有哪些即插即用設備;之后就可以按照指定的啟動順序從軟盤、硬盤或者光驅啟動操作系統。
BIOS初始化完了,自然是要讀取主引導記錄。主引導記錄(MBR)的格式如下圖:啟動代碼限制為446字節以內,用于檢查分區表的正確性、尋找活動分區并將活動分區引導程序加載到內存;還有64字節用于存儲硬盤分區表,描述分區狀態和位置,最多表示4個分區,每個分區16字節;還有一個MBR結束標志字(0x55AA),占2字節,只有有這個結束字才認為這條MBR是合法的MBR并執行啟動代碼。
當主引導記錄讀完并正確執行啟動代碼后,活動分區引導扇區的內容就被讀入內存,分區引導扇區格式如下圖:首先是跳轉指令,這條跳轉指令是與平臺相關的,CPU不同則跳轉指令不同,跳轉至啟動代碼處;文件卷頭結構則記錄文件系統描述信息;結束標志也是0X55AA;啟動代碼則負責跳轉到加載程序,加載程序不存儲在引導扇區,而是存儲在磁盤中,只要在啟動代碼中記錄加載程序的位置即可。
活動分區引導扇區將加載程序讀入內存之后,加載程序的執行狀態如下圖:加載程序并不是直接讀取操作系統內核,而是從文件系統中讀取啟動配置信息;依據配置信息(不同的內核有不同的配置信息),確定啟動的內核或啟動參數(比如是安全模式啟動還是正常模式啟動);最后根據配置加載指定內核并轉到內核執行,此時將控制權轉移給了操作系統內核。
值的一提的是,以上所說為較早版本的系統啟動流程,隨著技術的發展,出現了UEFI(Unified Extensible Firmware Interface)統一可擴展固件接口,這是BIOS的接任者。里面有一些新的標準,比如MBR只能存儲4個分區的方式逐漸被淘汰,現在出現了GPT(Globally Unique Identifier Partition Table)進行替代;UEFI可以選擇啟用哪些固件等。
中斷、異常與系統調用
這一節講解中斷、異常與系統調用,這三者也可以總稱為中斷處理機制。首先我們需要知道這三個機制的背景:1. 計算機運行過程中,內核是被完全信任的第三方;2. 只有內核可以執行特權指令;3. 內核需要方便地為應用程序提供服務。
那么我們為什么需要這三個機制呢?也就是說這三個機制實現了什么功能呢?
中斷是為了處理外設IO的,比如當我有一個新的鍵盤和鼠標接入了計算機,或者我們在鍵盤上敲了鍵盤,此時即使計算機正在進行其他任務也需要對此作出快速響應,這就是中斷要做的事情;
當我程序運行中出現了異常,比如某個在分母上的變量運行過程中變為了0,也就是出現了除以0這種操作,這是不合法的,又比如說程序訪問一塊內存區,而這個內存區是不允許這個程序訪問的,在運行到這一行程序之前我們無法預測這一異常,因此我們需要對這種異常有一種處理機制;
系統調用則是為上層的應用程序提供了各種接口,方便應用程序使用系統提供的服務,而又規避安全風險問題,即應用程序只使用提供給他的服務,而不會胡亂修改內核。
中斷、異常與系統調用的結構圖如下圖:當有外部設備接入或者有輸入,則告知內核(插入中斷向量表),內核通過設備驅動與外部設備交互;當應用程序執行過程中出現異常,也插入中斷向量表,內核要么將異常解決掉,要么終止程序,并將程序占用的資源返還;應用程序可以直接或者通過函數庫間接地使用系統調用接口,插入中斷向量表,中斷向量表通過查詢系統調用表提供系統調用實現,并將結果返還。因此可以說內核與外界打交道的接口主要就是中斷、異常與系統調用。
從上圖也可以看到中斷、異常與系統調用的區別如下圖:系統調用是應用程序主動向操作系統發出的服務請求;異常是非法指令或其他原因導致當前指令執行失敗(如內存出錯)后的處理請求;中斷則是來自硬件設備的處理請求。
下圖是三者比較:
這三者也即中斷處理機制具體實現過程是怎樣的呢?實現過程可以分為硬件和軟件兩部分。首先是硬件部分:在CPU初始化過程中會設置中斷使能標志,即在CPU做好準備工作之后才會開始接受中斷請求,在此之前是不會有反應的;此外需要根據內部或外部事件設置中斷標志,記錄出現了一次中斷;依據中斷向量表中的內容知道中斷源是什么,并調用相應中斷服務例程。以上為硬件部分工作內容,接下來的內容是軟件部分:1. 現場保存(編譯器);2. 中斷服務處理(服務例程);3. 清除中斷標記(服務例程);4. 現場恢復(編譯器)。
那么如果一個中斷出現的時候又出現新的中斷會怎樣呢(此處中斷指中斷處理機制即包含中斷、異常、系統調用三者)?其中,硬件終端服務例程可以被打斷,比如不同硬件中斷源同時出現,此時可以按照優先級依次處理,而出現了非常重要的中斷比如電源出現問題時,則可以臨時禁止接收中斷請求,而被接收了的中斷請求會保持到CPU對中斷做出響應。異常服務例程也是可以被打斷的,比如異常執行中可能出現硬件中斷,例如處理異常時進行磁盤IO,此時磁盤出現了硬件中斷,則會打盹異常服務例程去進行硬件中斷處理。異常服務例程也是可以嵌套的,比如處理異常時又出現了缺頁異常,此時則會產生嵌套。