從按下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.
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,進而執行啟動腳本。 |