Linux 啓動詳解2

機器加電啓動後,BIOS開始檢測參數,如內存的大小,日期和時間,磁盤設備以及這些磁盤設備用來引導的順序,通常情況下,BIOS都是被配置成首先檢查
軟驅或者光驅(或兩者都檢查),然後再嘗試從硬盤引導。如果在這些可移動的設
備中,沒有找到可引導的介質,那麼BIOS通常是轉向第一塊硬盤最初的幾個扇區,
尋找用於裝載操作系統的指令。裝載操作系統的這個程序就是boot loader.
裏面的boot loader通常是lilo或者grub,從RedHatLinux 7.2起,GRUB(
GRand Unified Bootloader)取代LILO成爲了默認的啓動裝載程序。那麼啓動的時候
grub是如何被載入的呢
grub有幾個重要的文件,stage1,stage2,有的時候需要stage1.5.這些文件一般都
在/boot/grub文件夾下面.grub被載入通常包括以下幾個步驟:
1. 裝載基本的引導裝載程序(stage1),stage1很小,網上說是512字節,但是在我的系統上
   用 du   -b   /boot/grub/stage1 顯示的是1024個字節,不知道是不是grub版本不同的
   緣故還是我理解有誤.stage1通常位於主引導扇區裏面,對於硬盤就是MBR了,stage1的
   主要功能就是裝載第二引導程序(stage2).這主要是歸結於在主引導扇區中沒有足夠的
   空間用於其他東西了,我用的是grub 0.93,stage2文件的大小是 107520 bit.
2. 裝載第二引導裝載程序(stage2),這第二引導裝載程序實際上是引出更高級的功能, 
   以允許用戶裝載入一個特定的操作系統。在GRUB中,這步是讓用戶顯示一個菜單或
   是輸入。由於stage2很大,所以它一般位於文件系統之中(通常是boot所在的根
   分區).
   上面還提到了stage1.5這個文件,它的作用是什麼呢   你到/boot/grub目錄下看看,
fat_stage_1.5   e2fs_stage_1.5 xfs_stage_1.5等等,很容易猜想stage1.5和文件系統
有關係.有時候基本引導裝載程序(stage1)不能識別stage2所在的文件系統分區,那麼這
時候就需要stage1.5來連接stage1和stage2了.因此對於不同的文件系統就會有不同的
stage1.5.但是對於grub 0.93好像stage1.5並不是很重要,因爲我試過了,在沒有stage1.5
的情況下, 我把stage1安裝在軟盤的引導扇區內,然後把stage2放在格式化成ext2或者
fat格式的軟盤內,啓動的時候照常引導,並不需要e2fs_stage_1.5或者fat_stage_1.5.
下面是我的試驗:
#mkfs.ext2   /dev/fd0
#mount -t ext2   /dev/fd0   /mnt/floppy
#cd   /mnt/floppy
#mkdir boot
#cd   boot
#mkdir grub   (以上三步可用mkdir -p   boot/grub命令完成)
#cd grub
#cp   /boot/grub/{stage1,stage2,grub.conf}   ./
#cd; umount /mnt/floppy
以上幾步把軟盤格式化成ext2格式,然後把stage1,stage2,grub.conf這幾個啓動的
時候必須的文件拷貝到軟盤的指定目錄下.下面安裝grub到軟盤上.
#grub   (進入grub環境)
grub> install (fd0)/boot/grub/stage1 (fd0)   (fd0)/boot/grub/stage2
p (fd0)/boot/grub/grub.conf
以上這條命令也可以用下面的兩句代替
grub>root (fd0) #grub的根目錄所在的分區
grub>setup (fd0) #這一步就相當於上面的install命令
我在這裏解釋一下
install (fd0)/boot/grub/stage1 (fd0)   (fd0)/boot/grub/stage2 p
(fd0)/boot/grub/grub.conf 這條命令.
install
告訴GRUB將(fd0)/boot/grub/grub/stage1
安裝到軟驅的引導扇區(fd0).
(fd0)/boot/grub/stage2
告訴grub stage2這個文件所在的位置.
p 參數後面跟着(fd0)/boot/grub/grub.conf 告訴grub的配置文件所在的位置.
好了,讓BIOS從軟驅啓動,試一下,沒有e2fs_stage_1.5文件照樣能夠進入系統.
其實這就是一個小小的啓動盤啊.(瞭解了grub的運行,就簡單多了^_^)


   3. 現在我們已經到grub的開機選單這一步了,接下來grub所需要做的就是裝載在一個特
定分區上的操作系統,如linux內核。一旦GRUB從它的命令行或者配置文件中,接到開始
操作系統的正確指令,它就尋找必要的引導文件,然後把機器的控制權移交給操作系統.
    由於篇幅有限,避免冗長,grub的命令我就不多說了,網上很有多的資料,一個典型
完整的引導linux的命令如下:
    title 51base
          root(hd0,0)
          kernel /bzImage   ro   root=/dev/ram0
          initrd /initrd.img
    這裏有必要注意一下幾個問題:
    (1)grub的磁盤以及分區的命名方式和linux有所區別,第一個磁盤是從0開始,第一
個分區也是從0開始.譬如第一個硬盤的第5分區在linux下面是/dev/hda5 ,而在grub裏面
是(hd0,4).再如/dev/fd0在grub裏面是(fd0,0).(最後一句如有錯誤望提醒)
    (2)不管是IDE硬盤hda,hdb還是SCSI硬盤sda,sdb在grub裏面都是以hd方式命名.
譬如虛擬機裏面的/dev/sda2在grub裏面是(hd0,1),再如/dev/hdb7在grub裏面以(hd1,6)
命名.
    (3)要搞清楚上面兩個root的關係,root (hd0,0)中的root是grub命令,它用來指定
boot所在的分區作爲grub的根目錄.而root=/dev/ram0是kernel的參數,它告訴操作系統
內核加載完畢之後,真實的文件系統所在的設備.要注意grub的根目錄和文件系統的根
目錄的區別.
    再回到上面的幾行命令.
    kernel命令用來指定內核所在的位置,"/"代表(hd0,0),也就是grub的根目錄
    initrd命令用來指定初始化ram的img文件所在位置.


grub載入內核bzImage並展開到指定位置(應該是0x100000這個地方),同時載入
initrd.img到內存(不知道是什麼地方).
ps:
grub的任務至此就結束了,下面grub將機器的控制權轉交給操作系統(linux).
操作系統接到控制權之後,開始start_kernel,接着內核將initrd.img展開到/dev/ram0
爲臨時根文件系統,執行裏面的linuxrc文件.
P.這裏有必要說一下initrd的作用特別是它裏面的核心文件linuxrc的作用.
initrd是inital ram disk的宿寫.
當存在initrd的時候,機器啓動的過程大概是以下幾個步驟(當initrd這一行用
noinitrd 命令代替後,就不存在initrd了)
1)boot loader(grub)加載內核和initrd.img
2)內核將壓縮的initrd.img解壓成正常的ram disk並且釋放initrd所佔的內存空間
3)initrd作爲根目錄以讀寫方式被掛載
4)initrd裏面的文件linuxrc被執行
5)linuxrc掛載新的文件系統
6)linuxrc使用pivot_root系統調用指定新的根目錄並將現有的根目錄place到指定
位置.
7)在新的文件系統下正式init
8)initrd被卸載.
爲了便於理解,我將red hat linnux9 裏面的initrd-2.4.20-8.img拿出來分析一下.
這其實是一個壓縮了的文件,是以gz結尾的.
[root@localhost root]#cp   /boot/initrd-2.4.20-8.img   /mnt/initrd-2.4.20-8.gz
[root@localhost root]#gunzip /mnt/initrd-2.4.20-8.gz
[root@localhost root]#mount -o loop /mnt/initrd-2.4.20-8   /mnt/ram
[root@localhost root]#cd   /mnt/ram
[root@localhost ram]#ls
bin dev etc lib linuxrc loopfs   proc sbin sysroot
[root@localhost ram]#ls bin
insmod modprobe nash
[root@localhost ram]#ls lib
Buslogic.o   ext3.o   jbd.o   scsi_mod.o sd_mod.o
[root@localhost ram]ls   dev
console   null   ram   systty   tty1   tty2   tty3   tty4
sbin目錄是指向bin目錄的一個連接,其他目錄是空的.
[root@localhost ram]cat   linuxrc
#!/bin/nash
1.echo "Loading scsi_mod.o module"
2.insmod /lib/scsi_mod.o
3.echo "Loading sd_mod.o module"
4.insmod /lib/sd_mod.o
5.echo "Loading BusLogic.o module"
6.insmod /lib/BusLogic.o
7.echo "Loading jbd.o module"
8.insmod /lib/jbd.o
9.echo "Loading ext3.o module"
10.insmod /lib/ext3.o
11.echo Mounting /proc filesystem
12.mount -t proc /proc /proc
13.echo Creating block devices
14.mkdevices /dev
15.echo Creating root device
16.mkrootdev /dev/root
17.echo 0x0100 > /proc/sys/kernel/real-root-dev
18.echo Mounting root filesystem
19.mount -o defaults --ro -t ext3 /dev/root /sysroot
20.pivot_root /sysroot /sysroot/initrd
21.umount /initrd/proc
上面的編號是我爲了下面好說明加上去的.
首先我們必須注意的是這裏使用的shell是nash而不是bash,nash是專門爲linuxrc可執行
腳本設計的,因此你有必要看一看nash的man文檔.
1-10行是加載一些必要的模快.11-12行加載proc內核文件系統,13-14行利用nash內建的
命令mkdevices創建塊設備,mkdevices是根據/proc/partitions文件創建裏面列出的所有
塊設備.15-16行利用nash內建的命令mkrootdev,mkrootdev使它後面的參數/dev/root成
爲一個塊節點從而使得根分區設備被掛載,其中根分區設備由grub.conf裏面的kernel命
令後面所帶的參數root=決定,如果root=參數沒有被指定,/proc/sys/kernel/real-root-
dev文件將提供根分區設備號.17行將數字256寫入到後面的文件裏面去.18-19行掛載根文
件系統到/sysroot目錄下,/dev/root裏面的內容就是root=參數所指定的設備裏面的內容
20行調用pivot_root改變根目錄所在地並place舊的根目錄到指定的位置.21行卸載舊的
根目錄裏面的proc內核文件系統.
從這裏面我們總結一下linuxrc的作用:   (參考/usr/src/linux-2.4/Documenta
tion/initrd.txt文檔)
2)/linuxrc文件決定在掛載真正的文件系統之前所需完成的事情(譬如加載必要的網
絡驅動或者加載ext3文件系統).
3)/linuxrc加載必要的模塊.
4)/linuxrc掛載根文件系統
5)/linuxrc調用pivot_root來改變根目錄
關於initrd的用途可以查考上面提到的文檔,想知道linux系統是如何安裝的嗎 那裏
面由答案.
既然linuxrc的主要目的是加載模快用的,那如果我們的內核沒有動態的模塊而所需
的功能都是靜態編譯進內核的,那麼是不是可以不用linuxrc文件呢
答案是可以不用,在普通的linux操作系統裏面可以加入noinitrd選項以告知boot
loader 不使用initrd.如果我們做網關,因爲ram是我們的文件系統的載體,所以initrd
一行當然不能去掉,但是我們可以不用linuxrc文件,sysroot文件夾和initrd文件夾.
不信的話,試試看吧.
好了,initrd(linuxrc)已經介紹完了.
linuxrc執行完畢之後,系統就會以真正的根目錄正式init.
系統在/bin/或者/sbin目錄下找到init程式,然後根據它的配置文件/etc/fstab進行
初始化,最後調用mingetty程式啓動login完成引導.
    ps:init這一部分網上有很多的詳細資料所以我在這裏並沒有展開來說.
關於linux的啓動流程的筆記


一、從哪裏到哪裏
本文旨在描述linux中內核如何調用啓動,然後如何從img的文件系統切換到硬盤的過程。
描述起於:linux-2.6.11/init/main.c中函數 static int init(void * unused)
描述止於:/etc/rc.d/rc.sysinit文件的被調用


二、描寫流程
在linux代碼linux-2.6.11/init/main.c中init這個函數被調用時,初始啓動的文件
系統鏡像:/boot/initrd-2.6.11.12.img(以2.6.11.12內核爲例)已被grub加載到
內存中,並已掛載到根目錄上("/")。




1、我們先來看看initrd-2.6.11.12.img到底是個什麼東西:
[root@wj-server1 tmp]# cd /tmp
[root@wj-server1 tmp]# cp /boot/initrd-2.6.11.12.img /tmp/initrd-2.6.11.12.gz
[root@wj-server1 tmp]# gunzip initrd-2.6.11.12.gz
解壓縮後的文件爲:
[root@wj-server1 tmp]# ls -l initrd-2.6.11.12
-rw-r--r--   1 root root 846848   7月 31 17:01 initrd-2.6.11.12
是一個CPIO格式的文件,該文件格式是種文件鏡像讓我們將它解開到一個目錄中看看
其中的具體內容:
[root@wj-server1 tmp]# mkdir initrd
[root@wj-server1 tmp]# cd initrd
[root@wj-server1 initrd]# cpio -i < ../initrd-2.6.11.12
1654 blocks
[root@wj-server1 initrd]# ls
bin   bootsplash   dev   etc   init   lib   loopfs   proc   sbin   sys   sysroot
[root@wj-server1 initrd]# find .
.
./lib
./bin
./bin/nash
./bin/insmod
./bin/modprobe
./bin/hotplug
./etc
./dev
./dev/console
./dev/null
./dev/ram
./dev/systty
./dev/tty1
./dev/tty2
./dev/tty3
./dev/tty4
./loopfs
./proc
./sys
./sysroot
./sbin
./init
./bootsplash


可見該鏡像文件目錄中包括:


/bin 目錄下的四個用於啓動和切換到硬盤上的程序:
nash(用於處理根目錄下的/init腳本)、insmod和modprobe來加載內核驅動、hotplug用
於外設的拔插處理。


/dev 目錄下的八個設備文件


/init 是個nash的啓動腳本文件


/bootsplash 是內核打了bootsplash補丁後,會在對該文件進行讀取操作,然後將該文件
中包含的圖片文件在啓動時顯示。
[root@wj-server1 initrd]# dmesg | grep -i bootsplash
bootsplash 3.1.6-2004/03/31: looking for picture... silentjpeg size 36270 bytes,
...found (1024x768, 19600 bytes, v3).
內核的這個裝載信息就是在處理該文件。(具體的bootsplash的使用和創建這裏不細說)。


附:CPIO文件的打包
[root@wj-server1 initrd]# cd /tmp/initrd
[root@wj-server1 initrd]# rm ../initrd-2.6.11.12
[root@wj-server1 initrd]# find . | cpio -c -o > ../initrd-2.6.11.12
1654 blocks
[root@wj-server1 initrd]# gzip ../initrd-2.6.11.12
[root@wj-server1 initrd]# mv ../initrd-2.6.11.12.gz ../initrd-2.6.11.12.img




2、回到內核init函數中,看看如何調用/boot/initrd-2.6.11.12.img中/init腳本的
....
// 這裏判斷在grub裝載的/boot/initrd-2.6.11.12.img中是否有"/init"這個文件?
if (sys_access((const char __user *) "/init", 0) == 0)
execute_command = "/init"
else
....
// 如果有"/init"這個文件就先運行它。
if (execute_command)
run_init_process(execute_command);


run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");


panic("No init found. Try passing init= option to kernel");


由代碼我們看到kernel會先判斷並運行/boot/initrd-2.6.11.12.img中的/init文件,我們
來看看該/boot/initrd-2.6.11.12.img/init文件的內容,我們上面已將該文件展開到目錄
/tmp/initrd中:
[root@wj-server1 initrd]# cat ./init
#!/bin/nash     # 該文件是個nash的腳本文件


# 掛接proc文件系統
mount -t proc /proc /proc


# 不輸出nash調試信息,由/proc/cmdline決定,cat /proc/cmdline我的啓動參數
# 輸出ro root=/dev/hda3 vga=791 splash=silent,如果該命令行中帶了quiet參
# 數,則不輸出nash提示信息。
setquiet


# 提示信息(這裏提示因該放到上面去,mkinitrd-4.2.17-2mgc.rpm包中原來如是,
# 筆誤?爲什麼這裏牽涉到mkinitrd這個包類?因爲:/boot/initrd-2.6.11.12.img
# 文件由下面命令生成:mkinitrd /boot/initrd-2.6.11.12.img 2.6.11.12)
echo Mounted /proc filesystem


# 掛接sys文件系統
echo Mounting sysfs
mount -t sysfs /sys /sys


# 創建/dev臨時目錄
echo Creating /dev
mount -o mode=0755 -t tmpfs /dev /dev


# 創建設備文件(這些設備文件在切換到硬盤後,由/etc/rc.sysinit中start_udev
# 重新創建)
mknod /dev/console c 5 1
mknod /dev/null c 1 3
mknod /dev/zero c 1 5


# 新建僞終端目錄
mkdir /dev/pts
# 新建共享內存目錄
mkdir /dev/shm


# 這裏是調用的nash中的makedevs指令裝載硬盤等塊設備,不裝載其他設備只裝載
# 硬盤等塊設備
echo Starting udev
# 告訴內核當發現新拔插設備時用"/sbin/hotplug"程序來處理
echo -n "/sbin/hotplug" > /proc/sys/kernel/hotplug
makedevs
makedevs # 這裏多搞一次沒必要


echo Creating root device
# 由grub啓動命令行root=/dev/hda3來聯接設備/dev/root到root變量所指定的啓動
# 設備,見下面我的grub啓動參數:
# kernel /boot/vmlinuz-2.6.11.12   ro root=/dev/hda3 vga=791 splash=silent
mkrootdev /dev/root


# 掛接/dev/root目錄
echo Mounting root filesystem
mount -o defaults --ro -t ext3 /dev/root /sysroot


echo Switching to new root
# 切換根目錄到設備/dev/root目錄,運行完該命令根目錄"/"->"/dev/hda3"
switchroot --movedev /sysroot






由上面的註釋我們大概能夠明白./init腳本的基本運行流程:
a、裝載基本的內核系統文件和設備文件
b、根據grub的啓動命令行參數,判斷root根文件設備,參看/boot/grub/grub.conf文件中制定
的參數,該參數在內核啓動後可有cat /proc/cmdline顯示出來,nash和其他的一些程序也是通
過讀該系統文件來去內核啓動參數的。
c、在將從grub啓動參數中獲得根設備並將其與/dev/root設備聯接以後,通過nash的switchroot
指令將/dev/root設備掛接到根目錄上("/")


看看這樣操作後,留下的痕跡:
[root@wj-server1 initrd]# ls -l /dev/root
lrwxrwxrwx   1 root root 9   7月 31 12:06 /dev/root -> /dev/hda3
[root@wj-server1 initrd]# mount
/dev/hda3 on / type ext3 (rw)


到此爲止,已將硬盤設備裝載到根目錄下了,從而取代了原來有initrd.img文件的根位置。






3、再回頭看看內核中main.c中init函數,看看如何調用/sbin/init處理/etc/inittab文件
....
// 如果有"/init"這個文件就先運行它。
if (execute_command)
run_init_process(execute_command);


run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");


panic("No init found. Try passing init= option to kernel");


我們已經運行完run_init_process(execute_command);這裏了,然後繼續運行:
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");


/sbin/init這個文件在SysVinit-2.85-34mgc.rpm這個包中,該程序的主要處理代碼在文件:
sysvinit-2.85/src/init.c中,該文件主要查找和處理/etc/inittab文件,按照該文件的內容
依次做處理。


[root@wj-server1 initrd]# cat /etc/inittab
#
# inittab    This file describes how the INIT process should set up
#             the system in a certain run-level.
#
# Author:    Miquel van Smoorenburg, <[email protected]>
#             Modified for RHS Linux by Marc Ewing and Donnie Barnes
#


# Default runlevel. The runlevels used by RHS are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
id:5:initdefault:                         # /sbin/init 根據這裏判斷啓動的級別


# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit          # /sbin/init 會最先運行這個系統配置文件


l0:0:wait:/etc/rc.d/rc 0                    # /sbin/init 根據上面取得的級別運行相應
l1:1:wait:/etc/rc.d/rc 1                    # 目錄下的啓動腳本
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6


# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now # 設置關機熱鍵


# When our UPS tells us power has failed, assume we have a few minutes
# of power left.   Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly.
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"


# If power was restored before the shutdown kicked in, cancel it.
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"




# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1       # 建立6個登陸控制終端可以通過CTRL-ALT-F?
2:2345:respawn:/sbin/mingetty tty2       # 切換,'?'表示第幾個登陸控制檯,比如第1
3:2345:respawn:/sbin/mingetty tty3       # 個爲F1,第2個爲F2依次類推。F7爲X11控制
4:2345:respawn:/sbin/mingetty tty4       # 臺,後面就沒有了,所以在X下可以很靈活
5:2345:respawn:/sbin/mingetty tty5       # 的切換到控制檯下面操作。
6:2345:respawn:/sbin/mingetty tty6


# Run xdm in runlevel 5
x:5nce:/etc/X11/prefdm -nodaemon


通過內核中對/sbin/init的調用現在已經執行/etc/rc.d/rc.sysinit操作了。
3#大中小發表於 2005-7-31 19:07只看該作者
又記當magiclinux中initscripts因爲hal升級後bootsplash無法顯示滾動條,
將[ -x /sbin/start_udev ] && /sbin/start_udev提到startanimate之前,
就可以了,向這樣:
[ -x /sbin/start_udev ] && /sbin/start_udev


# Graphical bootup...
if [ "$BOOTUP" = "graphical" ]; then
if [ -w /proc/splash -a -e /proc/fb ]; then
   # Initialize bootsplash
   # TODO: Find out runlevel (-> number of startup scripts -> number of steps)
   runlevel=5
   export kscripts=`/bin/ls /etc/rc.d/rc$runlevel.d/K* |/usr/bin/wc |/bin/awk '{ print $1 }'`
   export sscripts=`/bin/ls /etc/rc.d/rc$runlevel.d/S* |/usr/bin/wc |/bin/awk '{ print $1 }'`
   export progress=1
   # HACK: count rc.sysinit stuff as startup scripts...
   sscripts=$(( $sscripts + 19 ))
   startanimate # start the animate
else
   # Kernel doesn't have bootsplash support --> switch to text
   export BOOTUP=color
fi
fi
一直以來,都認爲nash僅僅是作爲initrd.img當中加載驅動所使用的命令解釋器而已,並沒有對initrd.img當中的linuxrc腳本作一個深入的瞭解。例如,Redhat ES3當中的initrd.img內容(各系統略有不同)如下:
#!/bin/nash
echo "Loading scsi_mod.o module"
insmod /lib/scsi_mod.o
echo "Loading sd_mod.o module"
insmod /lib/sd_mod.o
echo "Loading libata.o module"
insmod /lib/libata.o
echo "Loading ata_piix.o module"
insmod /lib/ata_piix.o
echo "Loading jbd.o module"
insmod /lib/jbd.o
echo "Loading ext3.o module"
insmod /lib/ext3.o
echo Mounting /proc filesystem
mount -t proc /proc /proc
echo Creating block devices
mkdevices /dev
echo Creating root device
mkrootdev /dev/root
echo 0x0100 > /proc/sys/kernel/real-root-dev
echo Mounting root filesystem
mount -o defaults --ro -t ext3 /dev/root /sysroot
pivot_root /sysroot /sysroot/initrd
umount /initrd/proc
    前面是加載驅動部分,而後面是什麼用處,就沒有深究了。而我自己用的虛擬機裏通常會把驅動和文件系統支持編譯到內核,也壓根用不着initrd.img,所以把這個寶礦放了過去。今天幾個學生在Redhat 9的基礎上升級內核到2.6.19,並且是驅動、文件系統用模塊方式支持,終於出了問題。該來的還是要來的,應了那句話“出來混,遲早都是要還的”。學生們把前面的硬盤驅動、文件系統驅動都替換成2.6的模塊,後半部分在我的誤導下刪除(當然沒有刪除的也多半沒有成功),結果死活不能進入系統,報無法掛載根文件系統的kernel panic錯誤。查了半天,發現所有的驅動都加載正常,即便是在linuxrc當中加入bash獲得一個shell,檢查設備和文件系統的狀況也都是正常,可就是提示找不到根文件系統。
    開始引起我注意的是,通常在硬盤驅動和文件系統支持都完備的情況下它並沒有提示說VFS的root=xxxx錯誤,而直接提示的“VFS: Unable to mount root fs on (0, 0)”。這意味着它並未識別在grub.conf當中傳遞給kernel的root=/dev/xxx參數,因爲Linux的設備主設備號是不會爲0的。我曾懷疑root=參數沒有被接受,但卻注意到“Please append a correct "root=" boot option”的提示。
    爲了找出問題,我在linuxrc中用bash取得了一個shell,然後掛載proc文件系統後檢查/proc/sys/kernel/real_root_dev,發現其值爲0。這說明kernel得到的真實root文件系統的設備號不正確(當然有例外,注意後面的解釋)。我試着把正確的設備值(例如/dev/sda2用0x802)寫入,退出bash之後啓動果然正常了。這說明kernel解析參數root=/dev/xxx出現了錯誤。
    經過覈對內核代碼,發現kernel確實從root=這個參數傳遞中獲得真實的根文件系統的位置,但是這些/dev/xxx會被轉換成內核可識別的設備表示形式。轉換的過程中內核會試圖檢查傳遞進來的/dev/xxx設備是否存在,而這一步是kernel初始化過程中完成的。當我們把硬盤驅動編譯爲模塊時,kernel初始化過程中硬盤尚不能被識別(要等到kernel初始化完,使用initrd.img才能加載硬盤驅動),於是真實根文件系統的設備號就不能被正確轉換,使得其保留爲初始值0。
    那對於使用硬盤驅動的情況下如何解決這個問題呢?那就是initrd.img後半部分的工作。在加載完硬盤驅動後,腳本會用mkrootdev生成設備/dev/root,通過man nash你會發現這個命令的作用是根據內核傳遞參數當中的root=來創建對應該設備的節點,節點名就是/dev/root。之後它會把這個設備掛載到/sysroot這個位置,然後用pivot_root這個根交換的命令把真正運行的根文件系統切換到/sysroot之下,而運行linuxrc的initrd的文件系統將被掛載到真正的根系統下的initrd目錄。至於“echo 0x0100 > /proc/sys/kernel/real-root-dev”這個命令,看起來是告訴內核真正的文件系統是/dev/ram0,其實是利用內核判斷使用/dev/ram0爲根文件系統時不再mount其他設備作爲根文件系統的分支,作了一個小小的技巧騙過內核。
    整個腳本的關鍵在於mkrootdev這個命令。它不僅能夠根據root=/dev/xxx來生成對應的設備節點,還能夠在碰到root=LABEL=/的情況下探測所有的硬盤分區,以便找到對應着卷標爲/的分區。這也解開了我一直沒弄清楚的爲什麼root=LABEL=xxx的參數有些環境可以用而有些卻不行的謎團。這個LABEL=/的解析根本不是內核完成的,而是initrd.img當中linuxrc腳本的mkrootdev命令來完成的。腳本的第二個關鍵在於它負責完成了本是由內核做的掛載真實根文件系統的動作,並對當前根(即initrd使用的內存文件系統)和真實根文件系統進行根切換,又利用欺騙讓內核誤認爲當前使用/dev/ram0作爲根文件系統可以不再掛載真實的根文件系統,可謂用心良苦啊 ^_^
    當然,如果完全不用initrd.img的機制,你會發現/proc/sys/kernel/real_root_dev的值也是0。這並不是內核也弄錯了,而是只有在使用initrd.img機制時,real_root_dev才反映了內核中記錄的真實根文件系統的值,雖然它不一定準確。
    最後,總結一下:
1、當硬盤驅動以模塊形式提供時root=xxx傳遞給內核的參數可能不會直接起作用,內核在檢查這個參數時可能會發現這個設備(因爲沒有加載驅動而)不存在,因此導致內核沒有接受root=xxx的參數。
2、在這種情況下,initrd.img機制的作用不單單是加載驅動這麼簡單,還肩負着把正確的真實根的設備位置告知內核的艱鉅任務。不過實現的方法有兩種:
2.1 把正確的根設備號寫入/proc/sys/kernel/real_root_dev,或者
2.2 自己掛載真正的根設備,並進行根交換把根系統切換到真實的根系統上,還要阻止內核去掛載它認爲的真實根系統
對於2.2,掛載真正的根系統用nash解釋器的內置命令mkrootdev完成,它可以根據傳遞給kernel的參數(估計讀了/proc/cmdline)獲得/dev/xxx的字符串,或者得到LABEL=xxx的字符串後查詢各個分區的卷標來得到對應的真實根設備號。
3、如果沒有使用initrd.img的機制,/proc/sys/kernel/real_root_dev的值沒有任何處理,因爲它只有在調用initrd.img的時候纔會被同步於內核中的操作對象。
    另外,redhat 9所帶的nash解釋器(版本3.4.42)在處理mkrootdev的流程上是有bug的。如果傳給kernel的參數使用root=/dev/xxx的形式,那麼nash會從/proc/sys/kernel/real_root_dev裏獲取用戶想使用的真實根文件系統位置,顯然這在我們上面的分析當中是0,也就是錯誤的;而如果參數傳遞是root=LABEL=/的形式,那麼nash的處理走的另外一個分支,它會通過解析所有分區的卷標來查找所要的真實根文件系統,並不再查詢/proc/sys/kernel/real_root_dev,這倒讓我們可以得到正確的結果。bug呀bug。
    不過FC6所帶的nash 5.1.19已經完全不是這個流程了,應當不存在這樣的bug。
本人,聽網上視頻的簡單筆記:
Linux 啓動詳解
1. BIOS 自檢,根據BIOS的設置順序,如以什麼方式硬盤引導,軟驅引導看系統是以什麼方式引導,內存地
址是從oxfff0開始
對硬件設備進行初媽始化。
如果是硬盤引導,MBR(512字節的一扇區),是磁盤的在0柱面的第一個扇區。


2.啓動grub, 或lilo,都屬於引導加載程序,都是在linux下的。
主要區別:
lilo,沒有交互命令界面,grub有。
lilo,不支持網絡引導,grub支持。
    加密grub:
1)用加密工具:
   /sbin/grub-md5-crypt 用來給grub加密
2)修改/etc/grub.conf 文件
title前加入一行 password: --md5 +已經MD5的字符串
3.加載內核,linux內核影像會被加載到內存
加載特定的內核,可以用kernel / +tab鍵
kernel /initrd/initrd+tab鍵自動補齊
4.執行init 進程,是所有進程的發起者與控制者,pid號爲1,第一個運行的進程。
  作用:
1). 是所有進程父進程
2). 進入運行某個特定級別的進程程序,對某個進程級別進行管理。
  ---- ps -ef 列出所有在執行的進程
   PPID 指進程的父進程
  
5. 通過inittab文件進行系統的初始化
1)id:3:initdefault:
運行級別:0:關閉;
         1:單用戶模式;
    2:多用戶模式,但不允許使用網絡
    3:多用戶模式,允許使用網絡 
    4:用戶可以自定義級別 
    5:圖形界面,允許使用網絡
    6:重新啓動
2)si:sysinit:/etc/rc.d/rc.sysinit 作用很多,如下一博客 /etc/rc.d/rc.sysinit的作用
 
    cd /etc/rc.d/rc3.d
  
   首先結束k開頭的K(ill),然後開始s(tart)開頭的,並是按s後接的數字大小來決定哪個先開啓
3) 執行/etc/rc.d/rc.local文件
4)執行login程序

linux啓動詳解1:http://blog.csdn.net/hepeng597/article/details/10033149

發佈了36 篇原創文章 · 獲贊 12 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章