1.簡介
Linux啟動過程指的是從加電到看到shell提示的這一段時間。
Linux啟動的過程可以大概分為幾個個階段,具體劃分為幾個階段則依靠具體的系統實現。一般來說,基於X86架構的系統可以分為3個階段;一些嵌入式的系統有的分為兩個階段,有的分為3個階段。
很多介紹linux系統啟動的文章對細節部分都描述的不清楚,那就讓我來詳細的說一下吧,如果由錯誤,請多包含了:)
2. 啟動過程之上電與加載引導程序
無論是哪種系統,linux啟動的第一階段總是CPU上電初始化的階段。
CPU在上電之後首先是自檢的過程,當這個過程完成之後,CPU就會跳轉到一個固定的地址,從這個地址開始執行代碼。這個固定的地址根據CPU的不同而不同,比如PC中的這個地址是0XFFFF0。而這個固定的地址通常是一些只讀的或者可讀寫的閃存,CPU通過總線和這些閃存連接,並能夠通過尋址機制找到這些閃存的地址。
在普通PC中,BIOS系統是一個‘加載第一個引導程序’的系統,PC的CPU上電後執行的第一行代碼總是BIOS中的閃存所保存的代碼,這段代碼可以看作是一個啟動環境,它完成的工作有兩部分:基本硬件加電檢測與本地設備的枚舉與初始化。當完成加電檢測後,這段BIOS代碼(完成加電檢測的代碼)會被從內存中清除,但是BIOS的系統運行時服務代碼開始運行,這段代碼檢測CMOS的配置,其實也就是看看用戶配置的從哪個設備啟動,當BIOS的運行時服務代碼找到用戶配置的啟動設備後,就從此設備中將第一個引導程序的代碼拷貝到RAM中,至此,BIOS的任務順利完成。剩下的事情交給‘第一個引導程序’來繼續吧。需要注意的是這個所謂的‘第一個引導程序‘肯定保存在用戶選擇的設備中,並且BIOS的運行時服務代碼知道這個引導程序保存的地址,要不怎麼把它搞到RAM中去啊?
而在嵌入式系統中可能沒有BIOS這樣的系統,但是肯定也有一塊類似的閃存/ROM,CPU可以從這個ROM的地址上開始執行代碼,而這段代碼肯定是一個啟動環境,也就是一段特殊的程序了,比如U-BOOT什麼的。這段程序肯定保存在閃存的固定位置,要不您讓CPU怎麼找?這段程序干什麼呢?它們提供了將Linux系統映象下載到閃存並繼續執行的方法,除了可以存儲並引導Linux映象之外,這些程序還可能執行一定級別的系統測試和硬件初始化過程。嵌入式系統的這段啟動環境代碼就類似與PC中的‘第一個引導程序’代碼,也就是和硬盤MBR中包含的主引導程序類似。
在PC Linux啟動中,當BIOS發現是由硬盤引導系統後,就找到此硬盤的MBR,將MBR中保存的引導程序加載到RAM中,然後將CPU的控制權交給MBR中的這段代碼,BIOS的任務到此算是全部完成了。所以說,BIOS從上電到現在忙活了大半天,主要目的就是為了找到引導設備,並將引導設備的引導程序加載到RAM中來運行。BIOS好人啊!
3.第一階段引導程序
MBR中的代碼就是所謂的‘第一階段引導程序’。
它分為三個部分,第一部分是真正的代碼,即BootLoader代碼部分,就是一段可執行的代碼;第二部分是一個64字節的分區表,包含四個分區記錄;第三部分是結束標識符(0XAA55),用來做MBR的有效性檢測。
‘第一階段引導程序’的作用是查找並加載‘第二階段引導程序’。它通過在分區表中查找一個活動分區來實現此功能。在MBR的第二部分所標識的四個分區中,只有一個是活動分區,通常這個活動分區記錄包含了真正分區的信息,Bootloader通過這個信息找到真正的分區,然後從這個分區將‘第二階段引導程序’加載進RAM中。
4.第二階段引導程序
第二階段引導程序可以認為是引導過程的最後一步了,它的核心任務就是加載Linux內核與可選的初始化RAM盤。
在PC環境中,現在通常使用grub來實現引導程序。Grub相對應lilo來說一個最大的優點就是grub可以識別硬盤分區。其實在第一階段引導程序加載第二階段引導程序之前,第一階段引導程序會先引導一個第1。5階段引導程序進入RAM執行,這段代碼可以理解為包含linux系統映象的特殊文件系統,當此引導程序運行後,就會加載第二階段引導程序了。第二階段引導程序主要是grub的工作,就是使用我們事先配置好的grub來加載相應的內核映象與初始化RAM盤到內存中。
當內核影響與初始化RAM盤加載成功後,第二階段引導程序的任務也就勝利完成了,剩下的活都交給內核映象吧。
5.內核啟動過程
當第二階段引導程序完成任務,內核映象取得CPU控制權後,內核的旅程就開始了,終於等到這一天了!
可惜,此時的內核映象並不是一個可執行的程序,而是一個壓縮過的程序,使用zlib壓縮而成。其實此內核映象包括兩個部分,最前端的部分是一小段的可執行代碼,後面才是真正的內核映象。前端部分的主要工作是初始化少量的硬件設置,然後解壓縮內核映象並將解壓縮後的內核放入高端內存中。此時如果發現由初始化RAM盤,則將其移入內存中,暫時不用。然後,解壓縮後的內核取得了CPU的控制權,內核開始行動了!
內核啟動後,首先對頁表進行初始化,並啟動內存分頁功能,然後檢測CPU類型,然後調用start_kernel()函數,進入與體系無關的初始化部分,包括內存配置,加載初始化RAM盤,最後啟動init函數,至此內核啟動階段就結束了。