理解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一下, 這篇文章就不錯。

硬盤

    就物理組成來講,一個硬盤封裝裏有多個 盤片(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。之後有機會再概括。


圖1 有創建RAM Disk可能的啓動流程

 
圖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.

6,加載內核模塊(module)
    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,進而執行啓動腳本。
相關文章
相關標籤/搜索