深入理解linux啟動過程

lpwebnet發表於2014-02-08
深入理解linux啟動過程
很多年前學過的文章,在拿出來,加深一下印象:

linux系統的啟動過程是由很多步驟組成的,但是,無論你是啟動一個標準的x86桌面計算機,還是一個嵌入式PowerPC的目標板,大多數的流程是驚人得相似的。這篇文章,探索了linux從最初的啟動準備到使用者空間中某個程式被開啟之間的啟動過程,跟隨這個流程,你還能學到其他許多與啟動有關的知識,例如,boot loaders,核心解壓縮,初始化記憶體盤,以及其他一些linux啟動的部分。[@more@]在很早的時候,啟動一個計算機意味著去喂那些包含啟動程式的紙帶,或者透過手工使用前皮膚那密密麻麻的地址/資料/控制開關來載入啟動程式,但是,簡化這個流程並非必須的。

讓我們首先從一個較高的高度上來對linux的啟動做一個全面的分析,然後,我們會回顧一下,在每一個獨立的過程中,發生了什麼事情。在這途中的參考原始碼將幫助你在核心樹中不至於迷失方向,並能繼續深入挖掘下去。

一、 概述 圖1將是對啟動過程的整體回顧:
當系統一開始被啟動,或者重新啟動時,處理器將在眾所周知的位置執行程式碼。在個人電腦上,這個位置處於基本輸入輸出系統中,也就是我們所說的BIOS,它是被儲存在主機板上的快閃記憶體中的。在嵌入式系統中,中央處理器(CPU)則將復位區域啟用,來開始執行flash或者ROM中的已知的程式。在其它情況中,基本的過程是一樣的。由於個人電腦提供了眾多靈活的可選裝置,BIOS必須確定,由哪一個裝置來執行啟動過程。在接下來的文章中我們將涉及這方面的更多的內容。
當找到啟動裝置時,第一階段所用的boot loader被裝載到RAM中並被執行。這裡的boot loader在大小上小於一個扇區的大小,也就是512位元組,而它的任務,就是載入第二階段的boot loader。
當負責第二階段的boot loader位於記憶體中並被執行時,通常會顯示一個一閃而過的螢幕,然後linux以及可選的初始化記憶體盤(一種臨時的根檔案系統,如果想得到具體的介紹,請訪問)會被裝載到儲存器中。當系統映象被載入時,第二階段的boot loader將把控制權轉交給核心映象,與此同時,核心開始自解壓並初始化。在這個階段,第二階段的boot loader會檢查系統的硬體,列舉那些附加的硬體裝置,掛載根裝置,之後載入需要的核心模組。完成之後,第一個使用者空間程式(init)開始執行,更高層次的系統初始化開始。
這就是從表面上看,linux的啟動過程。好了,現在,讓我們更進一步,更深入地探索linux啟動過程中的一些細節。

二、 系統的啟動
系統啟動的階段,依賴於linux在哪個硬體裝置上啟動。在嵌入式系統中,當系統被開啟或者重新啟動的時候,就要使用啟動載入的環境。這方面的例子包括U-BOOT,RedBoot,和Lucent推出的MicroMonitor。嵌入式平臺通常是繫結了啟動監視器的。這些程式位於目標硬體上flash儲存器的特定位置,提供了將linux核心映象下載到flash儲存器的方法,並在接下來的過程中執行它。除了擁有儲存和啟動linux映象的功能外,這些啟動監視器還能進行一定程度上的系統檢測和硬體初始化。在一個嵌入式的目標板中,這些啟動監視器通常覆蓋了第一階段與第二階段boot loader的功能。

/************************************************************************************************/
小知識:如何檢視你的MBR內容。如果你希望檢視你MBR的具體內容,請用以下命令:
# dd if=/dev/hda of=mbr.bin bs=512 count=1
# od -xa mbr.bin
需要以root身份執行的dd命令,讀取你的第一個整合電子驅動器或者IDE驅動器的前512位元組,並將他們寫入
mbr.bim檔案。od命令則是以十六進位制和ASCII碼形式列印出這個二進位制檔案
/************************************************************************************************/

在個人電腦中,linux的啟動是從0xFFFF0地址開始的。BIOS的第一步動作就是進行上電自檢(POST)。POST的工作是檢查硬體裝置。BIOS的第二步動作就是列舉本地裝置並初始化。
由於BIOS功能使用上的不同,它由兩個部分組成:POST碼runtime服務。POST完成後,它將從儲存器中被清除,但是BIOS runtime服務會被保留,用於目標作業系統。
為了啟動作業系統,BIOS的runtime服務將搜尋那些啟用狀態的或是可引導啟動的裝置,搜尋的順序則由CMOS設定決定(也就是我們平時所謂的在BIOS中設定的啟動順序)。一個軟碟機,一臺光碟機,一個硬碟上的分割槽,網路上的裝置甚至一個usb 快閃記憶體盤都可以作為一個啟動裝置。
當然,linux通常是從硬碟啟動的。硬碟上的MBR(主啟動記錄)包含有基本的boot loader,它是一個512位元組大小的扇區,位於磁碟的第一個扇區(0磁頭0磁軌1扇區)。當MBR被裝載到RAM中後,BIOS就會將控制權轉交給MBR。

三、 第一階段boot loader 
位於MBR中的主boot loader是一個512位元組的映象,其中不僅包含了程式程式碼,還包含了一個小的分割槽表,如圖2所示。最初的446位元組是主boot loader,它裡面就包含有可執行程式碼以及錯誤訊息文字。接下來的64位元組是分割槽表,其中包含有四個分割槽的各自的記錄(一個分割槽佔16位元組)。MBR透過特殊數字0xAA55(譯者注:在電子界中AA55確實是具有傳奇色彩的數字,想知道為什麼麼?將它展開成二進位制形式,看看有什麼規律)作為兩個位元組的結束標誌。0x55AA同時也是MBR有效的校驗確認。

主boot loader的工作是尋找並載入第二boot loader。它透過分析分割槽表,找出啟用分割槽來完成這個任務,當它找到一個啟用分割槽時,它將繼續掃描剩下的分割槽表中的分割槽,以便確認他們都是未啟用的。確認完畢後,啟用分割槽的啟動記錄從裝置中被讀到RAM,並被執行。

四、 第二階段boot loader
起著次作用,或者說是第二boot loader,可以更加形象得被稱為核心載入程式。這個階段的任務就是載入linux核心,以及可選的初始化記憶體盤。

/*******************************************************************/
小知識:GRUB階段的boot loaders
在/boot/grub目錄中包含有stage1,stage2和stage1.5的boot loaders,同時還有不少可選的loaders(例如,CD-ROM使用的就是iso9660_stage_1_5)
/*******************************************************************/

把第一階段和第二階段的boot loaders聯合起來,就是在x86個人電腦中,我們所說的linux loader(LILO)或者GRand Unified Bootloader(GRUB)。由於GRUB修正了一些LILO中存在的缺陷,因此下面就讓我們來看看GRUB(如果你希望得到更多的關於GRUB,LILO和與之相關話題的討論資源,請見文後的參考資料)
對於GRUB來說,一個比較好的方面就是它包含了linux檔案系統的知識。與LILO使用裸扇區不同的是,GRUB能夠從ext2或者ext3檔案系統中載入linux核心。它是透過將本來兩階段的boot loader轉換成三個階段的boot loader。在第一階段(MBR)中會啟動stage1.5的boot loader來理解linux核心映象中的特殊的檔案系統格式,例如,reiserfs_stage1-5(用於從reiserf日誌檔案系統中進行載入)或e2fs+stage1_5(用於從wxt2或ext3檔案系統進行載入)。當stage1.5的boot loader被載入並執行時,stage2 的boot loader才能被載入。
當stage2被載入時,GRUB能根據請求的情況顯示一個可選核心的清單(在 /etc/grub.conf 中進行定義,同時還有幾個軟符號連結 /etc/grub/menu.lst 和 /etc/grub.conf)。你可以選擇一個核心,修改其附加的核心引數。同時,你可以選擇使用命令列的shell來對啟動過程進行更深層次的手工控制。
在第二階段boot loader存在與記憶體中後,就可以對檔案系統進行查詢了,同時,預設的核心映象以及初始化記憶體盤映象也被載入到記憶體中。一切準備完畢之後,第二階段的boot loader就會呼叫核心映象。

五、 核心
(譯者注:在翻譯本章的時候,譯者發現IBM網站上已有譯好的文章,因此從本章開始以官方網站上的內容為主)。當核心映像被載入到記憶體中,並且階段 2 的引導載入程式釋放控制權之後,核心階段就開始了。核心映像並不是一個可執行的核心,而是一個壓縮過的核心映像。通常它是一個 zImage(壓縮映像,小於 512KB)或一個 bzImage(較大的壓縮映像,大於 512KB),它是提前使用 zlib 進行壓縮過的。在這個核心映像前面是一個例程,它實現少量硬體設定,並對核心映像中包含的核心進行解壓,然後將其放入高階記憶體中,如果有初始 RAM 磁碟映像,就會將它移動到記憶體中,並標明以後使用。然後該例程會呼叫核心,並開始啟動核心引導的過程。

當 bzImage(用於 i386 映像)被呼叫時,我們從 ./arch/i386/boot/head.S 的 start 彙編例程開始執行(主要流程圖請參看圖 3)。這個例程會執行一些基本的硬體設定,並呼叫 ./arch/i386/boot/compressed/head.S 中的 startup_32 例程。此例程會設定一個基本的環境(堆疊等),並清除 Block Started by Symbol(BSS)。然後呼叫一個叫做 decompress_kernel 的 C 函式(在 ./arch/i386/boot/compressed/misc.c 中)來解壓核心。當核心被解壓到記憶體中之後,就可以呼叫它了。這是另外一個 startup_32 函式,但是這個函式在 ./arch/i386/kernel/head.S 中。

在這個新的 startup_32 函式(也稱為清除程式或程式 0)中,會對頁表進行初始化,並啟用記憶體分頁功能。然後會為任何可選的浮點單元(FPU)檢測 CPU 的型別,並將其儲存起來供以後使用。然後呼叫 start_kernel 函式(在 init/main.c 中),它會將您帶入與體系結構無關的 Linux 核心部分。實際上,這就是 Linux 核心的 main 函式。
/*******************************************************************/
小知識:GRUB 中的手工引導
在 GRUB 命令列中,我們可以使用 initrd 映像引導一個特定的核心,方法如下:

grub> kernel /bzImage-2.6.14.2
[Linux-bzImage, setup=0x1400, size=0x29672e]

grub> initrd /initrd-2.6.14.2.img
[Linux-initrd @ 0x5f13000, 0xcc199 bytes]

grub> boot

Uncompressing Linux... Ok, booting the kernel.

如果您不知道要引導的核心的名稱,只需使用斜線(/)然後按下 Tab 鍵即可。GRUB 會顯示核心和 initrd 映像列表。
/*******************************************************************/

透過呼叫 start_kernel,會呼叫一系列初始化函式來設定中斷,執行進一步的記憶體配置,並載入初始 RAM 磁碟。最後,要呼叫 kernel_thread(在 arch/i386/kernel/process.c 中)來啟動 init 函式,這是第一個使用者空間程式(user-space process)。最後,啟動空任務,現在排程器就可以接管控制權了(在呼叫 cpu_idle 之後)。透過啟用中斷,搶佔式的排程器就可以週期性地接管控制權,從而提供多工處理能力。
在核心引導過程中,初始 RAM 磁碟(initrd)是由階段 2 引導載入程式載入到記憶體中的,它會被複制到 RAM 中並掛載到系統上。這個 initrd 會作為 RAM 中的臨時根檔案系統使用,並允許核心在沒有掛載任何物理磁碟的情況下完整地實現引導。由於與外圍裝置進行互動所需要的模組可能是 initrd 的一部分,因此核心可以非常小,但是仍然需要支援大量可能的硬體配置。在核心引導之後,就可以正式裝備根檔案系統了(透過 pivot_root):此時會將 initrd 根檔案系統解除安裝掉,並掛載真正的根檔案系統。
initrd 函式讓我們可以建立一個小型的 Linux 核心,其中包括作為可載入模組編譯的驅動程式。這些可載入的模組為核心提供了訪問磁碟和磁碟上的檔案系統的方法,併為其他硬體提供了驅動程式。由於根檔案系統是磁碟上的一個檔案系統,因此 initrd 函式會提供一種啟動方法來獲得對磁碟的訪問,並掛載真正的根檔案系統。在一個沒有硬碟的嵌入式環境中,initrd 可以是最終的根檔案系統,或者也可以透過網路檔案系統(NFS)來掛載最終的根檔案系統。

六、 init的介紹與結束語
當核心被引導並進行初始化之後,核心就可以啟動自己的第一個使用者空間應用程式了。這是第一個呼叫的使用標準 C 庫編譯的程式。在此之前,還沒有執行任何標準的 C 應用程式。
在桌面 Linux 系統上,第一個啟動的程式通常是 /sbin/init。但是這不是一定的。很少有嵌入式系統會需要使用 init 所提供的豐富初始化功能(這是透過 /etc/inittab 進行配置的)。在很多情況下,我們可以呼叫一個簡單的 shell 指令碼來啟動必需的嵌入式應用程式。 
與Linux 本身非常類似,Linux 的引導過程也非常靈活,可以支援眾多的處理器和硬體平臺。最初,載入引導載入程式提供了一種簡單的方法,不用任何花架子就可以引導 Linux。LILO 引導載入程式對引導能力進行了擴充,但是它卻缺少檔案系統的感知能力。最新一代的引導載入程式,例如 GRUB,允許 Linux 從一些檔案系統(從 Minix 到 Reise)上進行引導。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17172228/viewspace-1077958/,如需轉載,請註明出處,否則將追究法律責任。

相關文章