引導(dǎo) Linux? 系統(tǒng)的過(guò)程包括很多階段。不管您是引導(dǎo)一個(gè)標(biāo)準(zhǔn)的 x86 桌面系統(tǒng),還是引導(dǎo)一臺(tái)嵌入式的 PowerPC? 機(jī)器,很多流程都驚人地相似。本文將探索 Linux 的引導(dǎo)過(guò)程,從最初的引導(dǎo)到啟動(dòng)第一個(gè)用戶(hù)空間應(yīng)用程序。在本文介紹的過(guò)程中,您將學(xué)習(xí)到各種與引導(dǎo)有關(guān)的主題,例如引導(dǎo)加載程序、內(nèi)核解壓、初始 RAM 磁盤(pán)以及 Linux 引導(dǎo)的其他一些元素。
早期時(shí),啟動(dòng)一臺(tái)計(jì)算機(jī)意味著要給計(jì)算機(jī)喂一條包含引導(dǎo)程序的紙帶,或者手工使用前端面板地址/數(shù)據(jù)/控制開(kāi)關(guān)來(lái)加載引導(dǎo)程序。盡管目前的計(jì)算機(jī)已經(jīng)裝備了很多工具來(lái)簡(jiǎn)化引導(dǎo)過(guò)程,但是這一切并沒(méi)有對(duì)整個(gè)過(guò)程進(jìn)行必要的簡(jiǎn)化。
讓我們先從高級(jí)的視角來(lái)查看 Linux 引導(dǎo)過(guò)程,這樣就可以看到整個(gè)過(guò)程的全貌了。然后將回顧一下在各個(gè)步驟到底發(fā)生了什么。在整個(gè)過(guò)程中,參考一下內(nèi)核源代碼可以幫助我們更好地了解內(nèi)核源代碼樹(shù),并在以后對(duì)其進(jìn)行深入分析。
圖 1 是我們?cè)?20,000 英尺的高度看到的視圖。
當(dāng)系統(tǒng)首次引導(dǎo)時(shí),或系統(tǒng)被重置時(shí),處理器會(huì)執(zhí)行一個(gè)位于已知位置處的代碼。在個(gè)人計(jì)算機(jī)(PC)中,這個(gè)位置在基本輸入/輸出系統(tǒng)(BIOS)中,它保存在主板上的閃存中。嵌入式系統(tǒng)中的中央處理單元(CPU)會(huì)調(diào)用這個(gè)重置向量來(lái)啟動(dòng)一個(gè)位于閃存/ROM 中的已知地址處的程序。在這兩種情況下,結(jié)果都是相同的。因?yàn)?PC 提供了很多靈活性,BIOS 必須確定要使用哪個(gè)設(shè)備來(lái)引導(dǎo)系統(tǒng)。稍后我們將詳細(xì)介紹這個(gè)過(guò)程。
當(dāng)找到一個(gè)引導(dǎo)設(shè)備之后,第一階段的引導(dǎo)加載程序就被裝入 RAM 并執(zhí)行。這個(gè)引導(dǎo)加載程序在大小上小于 512 字節(jié)(一個(gè)扇區(qū)),其作用是加載第二階段的引導(dǎo)加載程序。
當(dāng)?shù)诙A段的引導(dǎo)加載程序被裝入 RAM 并執(zhí)行時(shí),通常會(huì)顯示一個(gè)動(dòng)畫(huà)屏幕,并將 Linux 和一個(gè)可選的初始 RAM 磁盤(pán)(臨時(shí)根文件系統(tǒng))加載到內(nèi)存中。在加載映像時(shí),第二階段的引導(dǎo)加載程序就會(huì)將控制權(quán)交給內(nèi)核映像,然后內(nèi)核就可以進(jìn)行解壓和初始化了。在這個(gè)階段中,第二階段的引導(dǎo)加載程序會(huì)檢測(cè)系統(tǒng)硬件、枚舉系統(tǒng)鏈接的硬件設(shè)備、掛載根設(shè)備,然后加載必要的內(nèi)核模塊。完成這些操作之后啟動(dòng)第一個(gè)用戶(hù)空間程序(init
),并執(zhí)行高級(jí)系統(tǒng)初始化工作。
這就是 Linux 引導(dǎo)的整個(gè)過(guò)程?,F(xiàn)在讓我們深入挖掘一下這個(gè)過(guò)程,并深入研究一下 Linux 引導(dǎo)過(guò)程的一些詳細(xì)信息。
![]() ![]() |
![]()
|
系統(tǒng)啟動(dòng)階段依賴(lài)于引導(dǎo) Linux 系統(tǒng)上的硬件。在嵌入式平臺(tái)中,當(dāng)系統(tǒng)加電或重置時(shí),會(huì)使用一個(gè)啟動(dòng)環(huán)境。這方面的例子包括 U-Boot、RedBoot 和 Lucent 的 MicroMonitor。嵌入式平臺(tái)通常都是與引導(dǎo)監(jiān)視器搭配銷(xiāo)售的。這些程序位于目標(biāo)硬件上的閃存中的某一段特殊區(qū)域,它們提供了將 Linux 內(nèi)核映像下載到閃存并繼續(xù)執(zhí)行的方法。除了可以存儲(chǔ)并引導(dǎo) Linux 映像之外,這些引導(dǎo)監(jiān)視器還執(zhí)行一定級(jí)別的系統(tǒng)測(cè)試和硬件初始化過(guò)程。在嵌入式平臺(tái)中,這些引導(dǎo)監(jiān)視器通常會(huì)涉及第一階段和第二階段的引導(dǎo)加載程序。
![]() |
|
在 PC 中,引導(dǎo) Linux 是從 BIOS 中的地址 0xFFFF0 處開(kāi)始的。BIOS 的第一個(gè)步驟是加電自檢(POST)。POST 的工作是對(duì)硬件進(jìn)行檢測(cè)。BIOS 的第二個(gè)步驟是進(jìn)行本地設(shè)備的枚舉和初始化。
給定 BIOS 功能的不同用法之后,BIOS 由兩部分組成:POST 代碼和運(yùn)行時(shí)服務(wù)。當(dāng) POST 完成之后,它被從內(nèi)存中清理了出來(lái),但是 BIOS 運(yùn)行時(shí)服務(wù)依然保留在內(nèi)存中,目標(biāo)操作系統(tǒng)可以使用這些服務(wù)。
要引導(dǎo)一個(gè)操作系統(tǒng),BIOS 運(yùn)行時(shí)會(huì)按照 CMOS 的設(shè)置定義的順序來(lái)搜索處于活動(dòng)狀態(tài)并且可以引導(dǎo)的設(shè)備。引導(dǎo)設(shè)備可以是軟盤(pán)、CD-ROM、硬盤(pán)上的某個(gè)分區(qū)、網(wǎng)絡(luò)上的某個(gè)設(shè)備,甚至是 USB 閃存。
通常,Linux 都是從硬盤(pán)上引導(dǎo)的,其中主引導(dǎo)記錄(MBR)中包含主引導(dǎo)加載程序。MBR 是一個(gè) 512 字節(jié)大小的扇區(qū),位于磁盤(pán)上的第一個(gè)扇區(qū)中(0 道 0 柱面 1 扇區(qū))。當(dāng) MBR 被加載到 RAM 中之后,BIOS 就會(huì)將控制權(quán)交給 MBR。
![]() ![]() |
![]()
|
MBR 中的主引導(dǎo)加載程序是一個(gè) 512 字節(jié)大小的映像,其中包含程序代碼和一個(gè)小分區(qū)表(參見(jiàn)圖 2)。前 446 個(gè)字節(jié)是主引導(dǎo)加載程序,其中包含可執(zhí)行代碼和錯(cuò)誤消息文本。接下來(lái)的 64 個(gè)字節(jié)是分區(qū)表,其中包含 4 個(gè)分區(qū)的記錄(每個(gè)記錄的大小是 16 個(gè)字節(jié))。MBR 以?xún)蓚€(gè)特殊數(shù)字的字節(jié)(0xAA55)結(jié)束。這個(gè)數(shù)字會(huì)用來(lái)進(jìn)行 MBR 的有效性檢查。
主引導(dǎo)加載程序的工作是查找并加載次引導(dǎo)加載程序(第二階段)。它是通過(guò)在分區(qū)表中查找一個(gè)活動(dòng)分區(qū)來(lái)實(shí)現(xiàn)這種功能的。當(dāng)找到一個(gè)活動(dòng)分區(qū)時(shí),它會(huì)掃描分區(qū)表中的其他分區(qū),以確保它們都不是活動(dòng)的。當(dāng)這個(gè)過(guò)程驗(yàn)證完成之后,就將活動(dòng)分區(qū)的引導(dǎo)記錄從這個(gè)設(shè)備中讀入 RAM 中并執(zhí)行它。
![]() ![]() |
![]()
|
次引導(dǎo)加載程序(第二階段引導(dǎo)加載程序)可以更形象地稱(chēng)為內(nèi)核加載程序。這個(gè)階段的任務(wù)是加載 Linux 內(nèi)核和可選的初始 RAM 磁盤(pán)。
![]() |
|
在 x86 PC 環(huán)境中,第一階段和第二階段的引導(dǎo)加載程序一起稱(chēng)為 Linux Loader(LILO)或 GRand Unified Bootloader(GRUB)。由于 LILO 有一些缺點(diǎn),而 GRUB 克服了這些缺點(diǎn),因此下面讓我們就來(lái)看一下 GRUB。(有關(guān) GRUB、LILO 和相關(guān)主題的更多內(nèi)容,請(qǐng)參閱本文后面的 參考資料 部分的內(nèi)容。)
關(guān)于 GRUB,很好的一件事情是它包含了有關(guān) Linux 文件系統(tǒng)的知識(shí)。GRUB 不像 LILO 一樣使用裸扇區(qū),而是可以從 ext2 或 ext3 文件系統(tǒng)中加載 Linux 內(nèi)核。它是通過(guò)將兩階段的引導(dǎo)加載程序轉(zhuǎn)換成三階段的引導(dǎo)加載程序來(lái)實(shí)現(xiàn)這項(xiàng)功能的。階段 1 (MBR)引導(dǎo)了一個(gè)階段 1.5 的引導(dǎo)加載程序,它可以理解包含 Linux 內(nèi)核映像的特殊文件系統(tǒng)。這方面的例子包括 reiserfs_stage1_5
(要從 Reiser 日志文件系統(tǒng)上進(jìn)行加載)或 e2fs_stage1_5
(要從 ext2 或 ext3 文件系統(tǒng)上進(jìn)行加載)。當(dāng)階段 1.5 的引導(dǎo)加載程序被加載并運(yùn)行時(shí),階段 2 的引導(dǎo)加載程序就可以進(jìn)行加載了。
當(dāng)階段 2 加載之后,GRUB 就可以在請(qǐng)求時(shí)顯示可用內(nèi)核列表(在 /etc/grub.conf
中進(jìn)行定義,同時(shí)還有幾個(gè)軟符號(hào)鏈接 /etc/grub/menu.lst
和 /etc/grub.conf
)。我們可以選擇內(nèi)核甚至修改附加內(nèi)核參數(shù)。另外,我們也可以使用一個(gè)命令行的 shell 對(duì)引導(dǎo)過(guò)程進(jìn)行高級(jí)手工控制。
將第二階段的引導(dǎo)加載程序加載到內(nèi)存中之后,就可以對(duì)文件系統(tǒng)進(jìn)行查詢(xún)了,并將默認(rèn)的內(nèi)核映像和 initrd
映像加載到內(nèi)存中。當(dāng)這些映像文件準(zhǔn)備好之后,階段 2 的引導(dǎo)加載程序就可以調(diào)用內(nèi)核映像了。
![]() ![]() |
![]()
|
![]() |
|
當(dāng)內(nèi)核映像被加載到內(nèi)存中,并且階段 2 的引導(dǎo)加載程序釋放控制權(quán)之后,內(nèi)核階段就開(kāi)始了。內(nèi)核映像并不是一個(gè)可執(zhí)行的內(nèi)核,而是一個(gè)壓縮過(guò)的內(nèi)核映像。通常它是一個(gè) zImage(壓縮映像,小于 512KB)或一個(gè) bzImage(較大的壓縮映像,大于 512KB),它是提前使用 zlib 進(jìn)行壓縮過(guò)的。在這個(gè)內(nèi)核映像前面是一個(gè)例程,它實(shí)現(xiàn)少量硬件設(shè)置,并對(duì)內(nèi)核映像中包含的內(nèi)核進(jìn)行解壓,然后將其放入高端內(nèi)存中,如果有初始 RAM 磁盤(pán)映像,就會(huì)將它移動(dòng)到內(nèi)存中,并標(biāo)明以后使用。然后該例程會(huì)調(diào)用內(nèi)核,并開(kāi)始啟動(dòng)內(nèi)核引導(dǎo)的過(guò)程。
當(dāng) bzImage(用于 i386 映像)被調(diào)用時(shí),我們從 ./arch/i386/boot/head.S
的 start
匯編例程開(kāi)始執(zhí)行(主要流程圖請(qǐng)參看圖 3)。這個(gè)例程會(huì)執(zhí)行一些基本的硬件設(shè)置,并調(diào)用 ./arch/i386/boot/compressed/head.S
中的 startup_32
例程。此例程會(huì)設(shè)置一個(gè)基本的環(huán)境(堆棧等),并清除 Block Started by Symbol(BSS)。然后調(diào)用一個(gè)叫做 decompress_kernel
的 C 函數(shù)(在 ./arch/i386/boot/compressed/misc.c
中)來(lái)解壓內(nèi)核。當(dāng)內(nèi)核被解壓到內(nèi)存中之后,就可以調(diào)用它了。這是另外一個(gè) startup_32
函數(shù),但是這個(gè)函數(shù)在 ./arch/i386/kernel/head.S
中。
在這個(gè)新的 startup_32
函數(shù)(也稱(chēng)為清除程序或進(jìn)程 0)中,會(huì)對(duì)頁(yè)表進(jìn)行初始化,并啟用內(nèi)存分頁(yè)功能。然后會(huì)為任何可選的浮點(diǎn)單元(FPU)檢測(cè) CPU 的類(lèi)型,并將其存儲(chǔ)起來(lái)供以后使用。然后調(diào)用 start_kernel
函數(shù)(在 init/main.c
中),它會(huì)將您帶入與體系結(jié)構(gòu)無(wú)關(guān)的 Linux 內(nèi)核部分。實(shí)際上,這就是 Linux 內(nèi)核的 main
函數(shù)。
通過(guò)調(diào)用 start_kernel
,會(huì)調(diào)用一系列初始化函數(shù)來(lái)設(shè)置中斷,執(zhí)行進(jìn)一步的內(nèi)存配置,并加載初始 RAM 磁盤(pán)。最后,要調(diào)用 kernel_thread
(在 arch/i386/kernel/process.c
中)來(lái)啟動(dòng) init
函數(shù),這是第一個(gè)用戶(hù)空間進(jìn)程(user-space process)。最后,啟動(dòng)空任務(wù),現(xiàn)在調(diào)度器就可以接管控制權(quán)了(在調(diào)用 cpu_idle
之后)。通過(guò)啟用中斷,搶占式的調(diào)度器就可以周期性地接管控制權(quán),從而提供多任務(wù)處理能力。
在內(nèi)核引導(dǎo)過(guò)程中,初始 RAM 磁盤(pán)(initrd
)是由階段 2 引導(dǎo)加載程序加載到內(nèi)存中的,它會(huì)被復(fù)制到 RAM 中并掛載到系統(tǒng)上。這個(gè) initrd
會(huì)作為 RAM 中的臨時(shí)根文件系統(tǒng)使用,并允許內(nèi)核在沒(méi)有掛載任何物理磁盤(pán)的情況下完整地實(shí)現(xiàn)引導(dǎo)。由于與外圍設(shè)備進(jìn)行交互所需要的模塊可能是 initrd
的一部分,因此內(nèi)核可以非常小,但是仍然需要支持大量可能的硬件配置。在內(nèi)核引導(dǎo)之后,就可以正式裝備根文件系統(tǒng)了(通過(guò) pivot_root
):此時(shí)會(huì)將 initrd
根文件系統(tǒng)卸載掉,并掛載真正的根文件系統(tǒng)。
![]() |
|
initrd
函數(shù)讓我們可以創(chuàng)建一個(gè)小型的 Linux 內(nèi)核,其中包括作為可加載模塊編譯的驅(qū)動(dòng)程序。這些可加載的模塊為內(nèi)核提供了訪(fǎng)問(wèn)磁盤(pán)和磁盤(pán)上的文件系統(tǒng)的方法,并為其他硬件提供了驅(qū)動(dòng)程序。由于根文件系統(tǒng)是磁盤(pán)上的一個(gè)文件系統(tǒng),因此 initrd
函數(shù)會(huì)提供一種啟動(dòng)方法來(lái)獲得對(duì)磁盤(pán)的訪(fǎng)問(wèn),并掛載真正的根文件系統(tǒng)。在一個(gè)沒(méi)有硬盤(pán)的嵌入式環(huán)境中,initrd
可以是最終的根文件系統(tǒng),或者也可以通過(guò)網(wǎng)絡(luò)文件系統(tǒng)(NFS)來(lái)掛載最終的根文件系統(tǒng)。
![]() ![]() |
![]()
|
當(dāng)內(nèi)核被引導(dǎo)并進(jìn)行初始化之后,內(nèi)核就可以啟動(dòng)自己的第一個(gè)用戶(hù)空間應(yīng)用程序了。這是第一個(gè)調(diào)用的使用標(biāo)準(zhǔn) C 庫(kù)編譯的程序。在此之前,還沒(méi)有執(zhí)行任何標(biāo)準(zhǔn)的 C 應(yīng)用程序。
在桌面 Linux 系統(tǒng)上,第一個(gè)啟動(dòng)的程序通常是 /sbin/init
。但是這不是一定的。很少有嵌入式系統(tǒng)會(huì)需要使用 init
所提供的豐富初始化功能(這是通過(guò) /etc/inittab
進(jìn)行配置的)。在很多情況下,我們可以調(diào)用一個(gè)簡(jiǎn)單的 shell 腳本來(lái)啟動(dòng)必需的嵌入式應(yīng)用程序。
![]() ![]() |
![]()
|
與 Linux 本身非常類(lèi)似,Linux 的引導(dǎo)過(guò)程也非常靈活,可以支持眾多的處理器和硬件平臺(tái)。最初,加載引導(dǎo)加載程序提供了一種簡(jiǎn)單的方法,不用任何花架子就可以引導(dǎo) Linux。LILO 引導(dǎo)加載程序?qū)σ龑?dǎo)能力進(jìn)行了擴(kuò)充,但是它卻缺少文件系統(tǒng)的感知能力。最新一代的引導(dǎo)加載程序,例如 GRUB,允許 Linux 從一些文件系統(tǒng)(從 Minix 到 Reise)上進(jìn)行引導(dǎo)。
![]() ![]() |
![]()
|
聯(lián)系客服