此过程的就是为了检测一下外界的硬件设备是否可以正常运行,如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部落
复制代码