天天看點

了解Linux的啟動過程

從按下PC電源,到出現熟悉的bash提示符"$"或進入漂亮的KDE/GNOME桌面,這是我們每天開機必經的過程。那麼,在這短短幾十秒内,Linux是怎樣啟動的呢?本文介紹Linux的啟動過程。

    平台:PC機, Ubuntu 5.10

基礎知識

BIOS (Basic I/O System,基本輸入/輸出系統)

    BIOS,完整地說應該是ROM-BIOS,是隻讀存儲器基本輸入/輸出系統的簡寫,它實際上是被固化到計算機中的一組程式,為計算機提供最低級的、最直接的硬體控制。準确地說,BIOS是硬體與軟體程式之間的一個“轉換器”或者說是接口(雖然它本身也隻是一個程式),負責解決硬體的即時需求,并按軟體對硬體的操作要求具體執行。

  從功能上看,BIOS分為三個部分:

  1.自檢及初始化程式;

  2.硬體中斷處理;

  3.程式服務請求。

    這裡我們主要關注第一部分——自檢及初始化程式:這部分負責啟動計算機,具體有三個部分,第一個部分是用于計算機剛接通電源時對硬體部分的檢測,也叫做加電自檢(POST),功能是檢查計算機是否良好,例如記憶體有無故障等。第二個部分是初始化,包括建立中斷向量、設定寄存器、對一些外部裝置進行初始化和檢測等,其中很重要的一部分是BIOS設定,主要是對硬體設定的一些參數,當計算機啟動時會讀取這些參數,并和實際硬體設定進行比較,如果不符合,會影響系統的啟動。

  最後一個部分是引導程式,功能是引導DOS或其他作業系統。BIOS先從軟碟或硬碟的開始扇區讀取引導記錄,如果沒有找到,則會在顯示器上顯示沒有引導裝置,如果找到引導記錄會把計算機的控制權轉給引導記錄,由引導記錄把作業系統裝入計算機,在計算機啟動成功後,BIOS的這部分任務就完成了。

    關于BIOS的詳細介紹,可以google一下,這篇文章就不錯。

硬碟

了解Linux的啟動過程

    就實體組成來說,一個硬碟封裝裡有多個盤片(platter),每個盤面有兩個面(surface)。在盤片上都有一個磁頭(head)來進行硬碟盤片的讀/寫,盤片繞軸(spinder)旋轉一周時磁頭所走過的軌迹即磁道(track),所有盤片的同一磁道構成了磁柱(cylinder)。磁道又被分為多個扇區(sector),扇區是最小的磁盤存儲機關,即硬碟分區時的最小機關——通常為512KB。磁道由縫隙(gap)分開,gap中存儲的不是資料位,而是用來确認扇區的格式位。

MBR

    主引導扇區(MBR, Master Boot Recorder)是硬碟中最重要的部分,它記錄了硬碟的分區資訊、引導資訊。CU上面有一篇介紹MBR的文章。

注意這裡所說的MBR是指BIOS中指定的啟動裝置中的MBR。如果以軟碟啟動,則MBR是軟碟的第一個扇區。如果是硬碟,則是硬碟的第一個扇區。如果有多個硬碟呢?那麼就是BIOS中指定啟動硬碟的第一個扇區!

run-level

運作  $ less /etc/inittab

顯示下列資訊:

# /etc/init.d executes the S and K scripts upon change

# of runlevel.

#

# Runlevel 0 is halt.

# Runlevel 1 is single-user.

# Runlevels 2-5 are multi-user.

# Runlevel 6 is reboot.

    上面顯示的就是目前可用的登入模式,共有0~6中級别。常用的是3和5。

0:關機

1:單使用者模式(系統有問題時的登入模式,相當于WINDOWS的"安全模式“)

2:對于Debian/Ubuntuare來說,2~5都是相同的——多使用者圖形界面模式。對于其他發行版來說,3可能是多使用者文本模式,4為系統保留,5為多使用者圖形模式,具體的定義可以檢視該發行版對應的/etc/inittab檔案内容。

6:重新啟動

另外,還可能有"S"級,它等同于1的單使用者級别。

    運作 $ runlevel 可以檢視系統目前運作級别

如果把運作級别設成了0或6,想象會出現什麼情況?如何解決呢?

    WINDOWS在啟動時,如果按下F8,會出現“安全模式“、”正常啟動“、”MS-DOS“模式的選擇。相當于Linux run-level的1,5,3(不對應于Debian/Ubuntu)。

關于Debian/Ubuntu中的run-level,看這裡!

基本流程

1, 加載BIOS硬體資訊,并取得第一個開機裝置的代号。

2,加載第一個開機裝置中MBR的boot loader(即lilo, grub, spfdisk等)引導資訊。

3,加載Linux核心,核心開始解壓縮,并驅動硬體。

4,核心執行init程式,并獲得run-level資訊;

5,init 執行 /etc/init.d/rcS 程式;

6,加載核心子產品(module)

7,init 執行 對應run-level 級的腳本檔案( Scripts );

8,執行 /bin/login 程式,等待使用者登入;

9,使用者登入之後,開始以shell控制系統(如果以圖形界面登入,則運作圖形界面)。

下面具體介紹流程中的步驟:

1,加載BIOS

    系統上電時,最先讀取BIOS資訊。BIOS(Basic Input/Output System)是計算機與外設最底層的接口,它存儲了計算機啟動時最先加載的資料,包括:CPU類型、啟動裝置順序、硬碟大小/類型、晶片組工作狀态、外設I/O位址、PnP (Plug and Play,既插既用裝置)的開啟與否、記憶體時鐘等。

    讀取了BIOS設定值後,系統根據BIOS資料進行開機自我檢測(Power On Self Test, POST),對硬體進行初始化,并設定PnP裝置,指定啟動裝置,之後從磁盤的MBR中讀取Bootloader資料。

2,加載Boot Loader

    系統讀完BIOS之後,接着加載第一個引導磁盤的第一個扇區(MBR),boot loader就位于MBR中。此時,啟動工作的接力棒就交到了boot loader的手中。

常用的boot loader有lilo, grub, spfdisk等,現在最流行的是grub,我用的Ubuntu中,boot loader就是grub,本文假設boot loader是grub,其實基本原理都是一樣的。

    為什麼要在MBR中安裝boot loader呢?它到底有什麼作用?實際上,boot loader的作用就是加載OS核心。系統在啟動時,要讀取檔案以加載核心,必須能夠識别硬碟檔案系統,但這時候系統還在啟動過程中,對檔案系統資訊一無所知。boot loader就輔佐系統識别檔案格式,加載核心。boot loader不僅不光能夠識别Linux核心,而且能識别WINDOWS核心.是以,如果要安裝多系統,那麼要在MBR中安裝能支援這些系統檔案系統的 boot loader.

    如果是以grub啟動,加載它後,會有個選擇啟動那個OS的菜單,當你作出選擇後,grub就從被標明OS所在的扇區中加載相應的核心.

3,加載核心

    在grub的菜單標明啟動Linux後,系統從Linux所在的磁盤載入核心。核心一般位于/boot目錄中,比如,我的系統中核心為 /boot/vmlinuz-2.6.12-10-686。當然,可以有不同版本的核心位于/boot目錄中,可以通過grub菜單選擇啟動的核心版本。

$ uname -r  : 顯示目前運作的核心版本

    vmlinuz是可引導的、壓縮的核心。“vm”代表“Virtual Memory”。vmlinux是未壓縮的核心,vmlinuz是vmlinux的壓縮檔案.

    加載核心時還應該注意”虛拟硬碟“,即RAM DISK。以後有機會再歸納。

了解Linux的啟動過程

圖1 有建立RAM Disk可能的啟動流程

了解Linux的啟動過程

圖2 更明了的啟動流程

總之,boot loader現将Linux核心加載到記憶體中(可能基于initrd建立RAM DISK),然後将BIOS中關于裝置的資料傳遞給核心,核心建設裝置,加載相應的驅動程式.

4,init程序

    核心被加載之後,它執行的第一個程式就是/sbin/init.init 它利用 /etc/inittab配置檔案擷取開機等級 ( Run level ) 之外, 并基于run level 選擇開機時啟動的服務.

    通過 $ less /etc/inittab 檢視開機init讀取的開機配置檔案内容.注意下面這行:

    # The default runlevel.

    id:2:initdefault:

    Ubuntu預設的run level是2.正如前面所說,把它設定成2,3,4,5都是多使用者圖形模式登入.千萬别設成0或6!

關于init程序,這裡多說幾句(針對一般UNIX系統)

pid=0 : swapper程序,用于程序排程.它位于核心内部,是系統程序.

pid=1 : init程序, 有對應的程式/sbin/init,盡管運作它需要管理者權限,但實際上它是個使用者程序,不位于核心中.系統啟動時,核心被加載後調用init程序.

pid=2 : pagedaemon, 用于支援虛拟記憶體的頁.

    init程序還負責處理孤兒程序(父程序在子程序之前終止,則子程序成為孤兒程序, 此時,init繼承為他們的父程序).

5,init 執行 /etc/init.d/rcS 程式 (重點)

在Debian/Ubuntu系統中,初始化腳本是/etc/init.d/rcS,在Rad Hat中是/etc/rc.d/rc.sysinit。這裡面包含了裝入檔案系統,設定時間,打開交換分區,得到主機名等等内容。

    在前面提到的inittab檔案中,緊接runlevel之後有如下内容:

# Boot-time system configuration/initialization script.

# This is run first except when booting in emergency (-b) mode.

si::sysinit:/etc/init.d/rcS

    inittab的主要功能是描述引導及正常操作時,應該在何種運作等級下啟動什麼程式,每個運作等級的具體項目完全可以通常/etc/inittab來定義,但Debian有一個更健壯的方案sysvinit,它被認為是init最強大的應用程式之一。Debian組織inittab的方式是把運作等級的大部分定義從inittab中移出來,移到一個腳本層次中去。惟一直接從inittab啟動的程式隻有getty,它用于虛拟裝置上啟動登入提示符,保留它因為它們要求特殊處理,在inittab之外處理要困難得多。

  inittab來啟動所有軟體當然是可能的,但将所有配置寫在同一個檔案既不友善檢視也不友善維護,是以檔案裡會加上這許多行:

  l0:0:wait:/etc/init.d/rc 0

  l1:1:wait:/etc/init.d/rc 1

  l2:2:wait:/etc/init.d/rc 2

  l3:3:wait:/etc/init.d/rc 3

  l4:4:wait:/etc/init.d/rc 4

  l5:5:wait:/etc/init.d/rc 5

  l6:6:wait:/etc/init.d/rc 6

  這些行實際決定了系統在各個運作等級下的行為。它們如何做到的也許并不明顯,但至少我們知道主要意思:首先每行都有個符号ID lx,lx表示runlevel x;其次,每行隻在一個運作等級下激活,該運作等級對應着符号ID中的數字x。指令執行時,init停下來,直到程序結束。最後,每個指令行調用一個腳本 /etc/init.d/rc x,這裡x代表目前運作等級的數字。顯然各運作等級的具體任務在/etc/init.d/rcS腳本中安排。

init把從inittab中擷取的run-level值作為參數傳遞給rc

rc與rcS腳本的差別 (rc = run command)

rc   : This file is responsible for starting/stopping services when the runlevel changes.

rcS : Call all S??* scripts in /etc/rcS.d in numerical/alphabetical order.

    2.6的核心支援動态子產品的加載,關于核心子產品,我另外寫一篇。

7,init 執行 對應run-level 級的腳本檔案( Scripts )

    到目前為止,核心及其子產品被加載了,也完成了系統的初始化。現在要做的工作就是開啟系統服務。由于不同的run-level需要開啟的服務不同,是以系統為不同的run-level設定了不同的腳本。

$ ls -d /etc/rc*.d

        可以看到有rc0~rc6及rcS,共8個目錄。他們所包含的檔案都是連接配接,指向/etc/init.d/目錄中的檔案。比較各目錄中的檔案你會發現, rc2~rc5中的内容是相同的。正好驗證了Debian/Ubuntu中run-level 2~5是等效的。

以S開頭的表示在對應級别Start的服務,以K開頭表示Kill的服務。緊跟S或K之後的兩位數字決定了啟動順序,數字小的先運作。

關于rcS/rcS.d,我還不是很清楚,哪位大俠指點?

8,執行 /bin/login 程式,等待使用者登入

    這個就不用多說了。看過APUE的都知道login要讀取/etc/passwd檔案。關于passwd檔案,請參考APUE,p161 (第二版新版哦!)

OK,完畢,謝謝收看。有不足之處,懇請指正!

 在系統加電引導時,init從run-level = 0開始,一級一級往上運作到inittab中定義的預設run-level。在run-level過渡時,init将run-level值作為參數傳遞給rc,進而執行啟動腳本。

繼續閱讀