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