此過程的就是爲了檢測一下外界的硬件設備是否可以正常運行,如CPU,內存設備,硬盤等等這些硬件設備是否能夠正常工做。linux
BIOS對於常常基礎計算機的人應該不會陌生,特別是那些常常裝系統的人,它就是列出幾個選項,讓你選擇以什麼方式來啓動系統,常見的有硬盤啓動,光盤,以及網絡方式啓動。c#
這個步驟略有複雜,可是其實現的功能就是,引導加載系統中的核心文件,並提交到內存運行,它會列出一個grub菜單,其中的選項是咱們操做系統的內核,你選擇的內核文件會被加載至內存中運行。bash
引導加載器grub:找到內核文件,提供grub菜單網絡
1)Stage 1,讀取MBR,目的是爲了驅動stage2所在的分區(stage2上存放的是內核文件以及rootfs的文件系統驅動)
2)Stage 1.5,經過Stage1能夠識別到stage2所在的分區,然而分區上的文件系統是須要文件系統驅動程序的,stage 1.5就是爲了stage2提供文件系統驅動的(在0柱面1扇區後面的63個扇區中存儲)
3)經過stage1和1.5,stage2將被加載(通常爲/boot分區),此時stage2,將提供一個內核選擇菜單,而且stage2分區內還有一個ramdisk或者ramfs文件,其爲一個臨時的根文件系統,其中包含了真正的rootfs所須要的驅動文件,因此stage2及內核文件一般放置於一個基本的磁盤分區(通常爲/boot分區).
注意:ramdisk臨時根文件系統是在安裝操做系統後臨時生成的,它在安裝操做系統後,能掃描當前主機硬盤設備的型號,並找到相關驅動作成一個臨時根
複製代碼
代碼分析:app
[root@localhost testdir]# cp /boot/initramfs-2.6.32-642.el6.x86_64.img . //將/boot下的ramfs文件拷貝至當前目錄
[root@localhost testdir]# zcat initramfs-2.6.32-642.el6.x86_64.img |cpio -id //將其解壓縮
140023 blocks
[root@localhost testdir]# ls //其包含的內如以下,由於是臨時的根文件,因此目錄結構也相似於咱們的rootfs,其中包含rootfs所須要的文件系統的驅動
bin dracut-004-409.el6 init initqueue-settled lib netroot pre-trigger sbin tmp
cmdline emergency initqueue initqueue-timeout lib64 pre-mount pre-udev sys usr
dev etc initqueue-finished initramfs-2.6.32-642.el6.x86_64.img mount pre-pivot proc sysroot var
[root@localhost testdir]#
複製代碼
經過上面所選擇的內核文件,來將其加載至內存中解壓縮,分爲如下四個步驟ssh
1)探測可識別到的全部硬件設備。
2)加載硬件驅動程序(可能借助於ramdisk/ramfs加載驅動)
3)以只讀方式掛載根文件系統
4)運行用戶空間的第一個應用程序:/sbin/init
複製代碼
注意:其中Ramdisk/ramfs即stage2所在分區的rootfs文件系統驅動的文件,有了內核文件及所須要的rootfs的文件系統驅動,爲避免內核文件有bug或者人爲操做問題,先以只讀方式掛載rootfssvn
代碼分析:函數
1)根據init的配置文件獲取到運行級別信息,並獲取系統初始化腳本的文件路徑。(CentOS 5的init文件爲/etc/inittab,CentOS6將/etc/inittab文件拆分爲多個文件)學習
init的配置文件:
CentOS 5:採用SysV init方式,其特色是啓動用戶空間的服務程序,一般經過腳本進行,有依賴關係的服務將被串行啓動,這也致使了CentOS 5的啓動過程至關緩慢,配置文件爲/etc/inittab
CentOS 6:採用Upstart的方式,其特色是相似於並行啓動;配置文件:/etc/inittab,/etc/init/*.conf
複製代碼
2)讀取系統初始化腳本/etc/rc.d/rc.sysinit,並按照腳本內容執行,做用以下:ui
(1) 設置主機名
(2) 設置歡迎信息
(3) 激活udev和selinux
(4) 掛載/etc/fstab文件中定義的文件系統
(5) 檢測根文件系統,並以讀寫方式從新掛載根文件系統
(6) 設置系統時鐘
(7) 激活swap設備
(8) 根據/etc/sysctl.conf文件設置內核參數
(9) 激活lvm及software raid設備
(10) 加載額外設備的驅動程序
(11) 清理操做
複製代碼
3)根據前面獲取的運行級別,運行/etc/rc.d/rc腳本文件
/etc/rc.d/目錄下有幾個rc#.d(#號數字,也就是表明運行級別),其目錄下文件爲連接文件,其指向/etc/init.d/下的服務腳本文家,根據在/etc/inittab獲取的默認運行級別和/etc/rc#.d下的連接文件,來啓動和關閉系統的服務,想必如今也能聯想到了爲何不一樣級別下啓動的服務不相同,爲何有的服務開機啓動,有的卻關閉 /etc/rc#.d/下的連接文件以K或者S開頭,K表示開機要被中止的服務,S表示開機要被啓動的服務,並且服務腳本都會有一個優先級,
K*:K##*:##運行次序;數字越小,越先運行;數字越小的服務,一般爲依賴到別的服務
S*:S##*:##運行次序;數字越小,越先運行;數字越小的服務,一般爲被依賴到的服務
複製代碼
[root@localhost boot]# cd /etc/rc.d
[root@localhost rc.d]# ls
init.d rc rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.local rc.sysinit
[root@localhost rc.d]# cd rc.3.d
root@localhost rc.d]# cd rc3.d
[root@localhost rc3.d]# ls
K01dnsmasq K35vncserver K88wpa_supplicant S08ip6tables S14nfslock S26lm_sensors S95anacron
K02avahi-dnsconfd K50netconsole K89dund S08iptables S15mdmonitor S26lvm2-monitor S95atd
K02NetworkManager K50snmpd K89netplugd S08mcstrans S18rpcidmapd S28autofs S97rhnsd
K05conman K50snmptrapd K89pand S10network S19rpcgssd S55sshd S97rhsmcertd
K05saslauthd K69rpcsvcgssd K89rdisc S11auditd S22messagebus S56cups S97yum-updatesd
K05wdaemon K73ypbind K99readahead_later S12restorecond S25bluetooth S56rawdevices S98avahi-daemon
K10psacct K74ipmi S00microcode_ctl S12syslog S25netfs S57vmware-tools-thinprint S99firstboot
K15httpd K74nscd S03vmware-tools S13cpuspeed S25pcscd S80sendmail S99local
K15svnserve K74ntpd S04readahead_early S13irqbalance S26acpid S85gpm S99smartd
K20nfs K85mdmpd S05kudzu S13iscsi S26haldaemon S90crond
K24irda K87multipathd S07iscsid S13portmap S26hidd S90xfs
複製代碼
[root@localhost rc3.d]# find /etc/ -name *local
/etc/rc.d/rc2.d/S99local
/etc/rc.d/rc5.d/S99local
/etc/rc.d/rc3.d/S99local
/etc/rc.d/rc4.d/S99local
複製代碼
代碼分析:如下爲CentOS 5中的/etc/inittab文件
id:3:initdefault: //獲取默認運行級別;3表明運行級別
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit //執行/etc/rc.d/rc.sysinit系統初始化腳本
// 根據前面獲取到的默認運行級別,執行/etc/rc.d/rc腳本文件
l0:0:wait:/etc/rc.d/rc 0
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
//下面爲/etc/rc.d/rc腳本中的一段代碼
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do //這是一個for循環,根據前面獲取的默認級別信息,來關閉/etc/rc#.d/下的服務
check_runlevel "$i" || continue
# Check if the subsystem is already up.
subsys=${i#/etc/rc$runlevel.d/K??}
[ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
|| continue
# Bring the subsystem down.
if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
$i stop //stop 關閉服務!
else
action $"Stopping $subsys: " $i stop
fi
done
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do //這也是個for循環,與上面相反,是啓動/etc/rc#.d/下面對應的腳本文件
check_runlevel "$i" || continue
# Check if the subsystem is already up.
subsys=${i#/etc/rc$runlevel.d/S??}
[ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
&& continue
# If we're in confirmation mode, get user confirmation
if [ -f /var/run/confirm ]; then
confirm $subsys
test $? = 1 && continue
fi
update_boot_stage "$subsys"
# Bring the subsystem up.
if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
export LC_ALL=C
exec $i start //啓動服務
fi
if LC_ALL=C egrep -q "^..*init.d/functions" $i \
|| [ "$subsys" = "single" -o "$subsys" = "local" ]; then
$i start //啓動服務
else
action $"Starting $subsys: " $i start
fi
done
複製代碼
下圖爲系統啓動時服務開啓的界面
根據前面獲取的默認運行級別來啓動終端,若是運行級別爲5,則啓動圖形界面
複製代碼
系統啓動流程結束!
複製代碼
在此以前,一直有幾點問題困惑着我,我對它們作了一下總結
1)內核文件在磁盤上,系統尚未啓動,系統尚未啓動,/目錄也沒有掛載,前面說先找到boot分區,可是boot分區也是在/的目錄下,/尚未,去哪找boot!?
問題解答:注意,此時系統去尋找boot分區下的grub菜單、內核文件及rootfs的驅動並非經過/目錄來尋找,由於此時的/尚未掛載,沒法找到/下面的boot目錄,而是直接去boot的那個磁盤分區去尋找所須要的文件,具體看一下代碼
[root@localhost ~]# cat /boot/grub/grub.conf //grub的配置文件
....省略.....
title CentOS 6 (2.6.32-642.el6.x86_64)
root (hd0,0) //hd0,0表示的是磁盤分區,即stage2所在的分區,此處的意義表示的是將hd0,0先設爲/目錄,那麼下面
的 kernel和initrd後面的根"/",表示的也是hd0,0,也就是stage2所在的磁盤分區。避免了經過/目錄來尋找
boot分區的難題.
//此時的/vmlinuz-2.6.32....其實就是boot分區下的vmlinuz文件
kernel /vmlinuz-2.6.32-642.el6.x86_64 ro root=/dev/mapper/vg0-root rd_NO_LUKS rd_NO_DM LANG=en_US.UTF-8 rd_LVM_LV=vg0/swap rd_NO_MD SYSFONT=latarcyrheb-sun16 crashkernel=auto rd_LVM_LV=vg0/root KEYBOARDTYPE=pc KEYTABLE=us rhgb crashkernel=auto quiet rhgb quiet
//一樣的 /initramfs-2.6.32...其實也是boot分區下的initfamfs文件,只不過直接經過磁盤分區去找到的
initrd /initramfs-2.6.32-642.el6.x86_64.img
[root@localhost ~]#
複製代碼
2)上面問題的繼續,即便你先加載boot分區,boot分區系統的系統驅動在哪裏呢
問題解答:從如下代碼得知,分區信息是從1柱面開始的,那麼0柱面被狗吃了麼?答案是沒有被狗吃,MBR存放在了0柱面,0磁道的第一個扇區內,可是它只佔據了512個字節,由於0柱麪包括了好多扇區,後面的扇區就是爲了存放/boot分區的文件系統驅動的。stage1->stage1.5->stage2這個過程就是爲了掛載/boot分區,而其中的stage1.5就是尋找/boot分區的文件系統驅動的。
[root@localhost ~]# df //df查看/目錄的掛在分區
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 7858552 5884648 1568264 79% /
/dev/sda1 295561 16787 263514 6% /boot
tmpfs 511932 0 511932 0% /dev/shm
[root@localhost ~]# fdisk -l //查看磁盤分區信息
Disk /dev/sda: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Device Boot Start End Blocks Id System
/dev/sda1 * 1 38 305203+ 83 Linux //注意到,起始柱面是從1柱面開始的,根據此前所學內容,第一個柱面是0柱面
/dev/sda2 39 1048 8112825 83 Linux
/dev/sda3 1049 1305 2064352+ 82 Linux swap / Solaris
複製代碼
3)加載內核後,爲避免bug或者人爲操做失誤,rootfs先以只讀方式掛載,只讀方式掛載怎麼寫數據呢?
問題解答:內核在讀取到init程序後,其中有一個系統初始化腳本,即/etc/rc.d/rc.sysinit腳本,其中有一段代碼以下,在這rootfs會被從新以讀寫方式掛載。
remount_needed() {
local state oldifs
[ "$READONLY" = "yes" ] && return 1
state=`LC_ALL=C awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`
oldifs=$IFS
IFS=","
for opt in $state ; do
if [ "$opt" = "rw" ]; then
IFS=$oldifs
return 1
fi
done
IFS=$oldifs
return 0
}
#Remount the root filesystem read-write.
update_boot_stage RCmountfs
if remount_needed ; then //根據前面定義的函數,來實現rootfs的讀寫掛載 mount -n -o remount,rw /
action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /
fi
複製代碼
俗話說的好,一圖抵千言,我將上面所述的啓動流程又畫了一幅圖,但願以更加清晰地描述CentOS的啓動流程。
喜歡我寫的東西的朋友能夠關注一下個人公衆號,上面有個人學習資源以及一些其餘福利。:Devops部落
複製代碼