微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

CentOS系统启动流程

概述:系统启动流程是Linux一个重要的内容,深入了解启动流程会对我们学习Linux起到一个顺水推舟的作用。因为CentOS 7改动较大,所以下面的内容只是针对CentOS 5和6来说的。下面进入正题。

启动流程:

第一步:POST加电自检

此过程的就是为了检测一下外界的硬件设备是否能够正常运行,如cpu,内存设备,硬盘等等这些硬件设备是否可以正常工作。

第二步:BIOS选择启动方式

BIOS对于经常基础计算机的人应该不会陌生,特别是那些经常装系统的人,它就是列出几个选项,让你选择以什么方式来启动系统,常见的有硬盘启动,光盘,以及网络方式启动。

第三步:BootLoader

这个步骤略有复杂,但是其实现的功能就是,引导加载系统中的核心文件,并提交到内存运行,它会列出一个grub菜单,其中的选项是我们操作系统的内核,你选择的内核文件会被加载至内存中运行。

引导加载器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临时根文件系统是在安装操作系统后临时生成的,它在安装操作系统后,能扫描当前主机硬盘设备的型号,并找到相关驱动做成一个临时根


wKiom1fTrJCgYwSaAAAo3bBzkxo171.png

代码分析:

[root@localhosttestdir]#cp/boot/initramfs-2.6.32-642.el6.x86_64.img.//将/boot下的ramfs文件拷贝至当前目录
[root@localhosttestdir]#zcatinitramfs-2.6.32-642.el6.x86_64.img|cpio-id//将其解压缩
140023blocks
[root@localhosttestdir]#ls//其包含的内如如下,因为是临时的根文件,所以目录结构也类似于我们的rootfs,其中包含rootfs所需要的文件系统的驱动
bindracut-004-409.el6initinitqueue-settledlibnetrootpre-triggersbintmp
cmdlineemergencyinitqueueinitqueue-timeoutlib64pre-mountpre-udevsysusr
devetcinitqueue-finishedinitramfs-2.6.32-642.el6.x86_64.imgmountpre-pivotprocsysrootvar
[root@localhosttestdir]#

第四步:加载内核文件

通过上面所选择的内核文件,来将其加载至内存中解压缩,分为以下四个步骤

1)探测可识别到的所有硬件设备。

2)加载硬件驱动程序(可能借助于ramdisk/ramfs加载驱动)

3)以只读方式挂载根文件系统

4)运行用户间的一个应用程序:/sbin/init

注意:其中Ramdisk/ramfs即stage2所在分区的rootfs文件系统驱动的文件,有了内核文件及所需要的rootfs的文件系统驱动,为避免内核文件有bug或者人为操作问题,先以只读方式挂载rootfs

代码分析:

wKioL1fTriPQjHNmAAB9ILqHZZQ654.png

第五步:Init程序初始化

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,并按照脚本内容执行,作用如下:
(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@localhostboot]#cd/etc/rc.d
[root@localhostrc.d]#ls
init.drcrc0.drc1.drc2.drc3.drc4.drc5.drc6.drc.localrc.sysinit
[root@localhostrc.d]#cdrc.3.d
root@localhostrc.d]#cdrc3.d
[root@localhostrc3.d]#ls
K01dnsmasqK35vncserverK88wpa_supplicantS08ip6tablesS14nfslockS26lm_sensoRSS95anacron
K02avahi-dnsconfdK50netconsoleK89dundS08iptablesS15mdmonitorS26lvm2-monitorS95atd
K02NetworkManagerK50snmpdK89netplugdS08mcstransS18rpcidmapdS28autofsS97rhnsd
K05conmanK50snmptrapdK89pandS10networkS19rpcgssdS55sshdS97rhsmcertd
K05saslauthdK69rpcsvcgssdK89rdiscS11auditdS22messagebusS56cupsS97yum-updatesd
K05wdaemonK73ypbindK99readahead_laterS12restorecondS25bluetoothS56rawdevicesS98avahi-daemon
K10psacctK74ipmiS00microcode_ctlS12syslogS25netfsS57vmware-tools-thinprintS99firstboot
K15httpdK74nscdS03vmware-toolsS13cpuspeedS25pcscdS80sendmailS99local
K15svnserveK74ntpdS04readahead_earlyS13irqbalanceS26acpidS85gpmS99smartd
K20nfsK85mdmpdS05kudzuS13iscsiS26haldaemonS90crond
K24irdaK87multipathdS07iscsidS13portmapS26hiddS90xfs

wKiom1fTuE3jn7HjAADGphozqfA218.png

注意:在2345级别的/etc/rc#.d目录下都会有一个rc.local,它其实也是一个链接文件链接到/etc/rc.d/rc.local,它并不是启动文件,而是一个普通的文件,不过它的优先级最小,所以最后启动,如果你想要开机做一些什么操作,可以写到这个脚本里面。

[root@localhostrc3.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

wKioL1fTuOCBMVgMAAA29uWUGEc613.png

代码分析:以下为CentOS 5中的/etc/inittab文件

id:3:initdefault://获取认运行级别;3代表运行级别

#Systeminitialization.
si::sysinit:/etc/rc.d/rc.sysinit//执行/etc/rc.d/rc.sysinit系统初始化脚本
//根据前面获取到的认运行级别,执行/etc/rc.d/rc脚本文件
l0:0:wait:/etc/rc.d/rc0
l1:1:wait:/etc/rc.d/rc1
l2:2:wait:/etc/rc.d/rc2
l3:3:wait:/etc/rc.d/rc3
l4:4:wait:/etc/rc.d/rc4
l5:5:wait:/etc/rc.d/rc5
l6:6:wait:/etc/rc.d/rc6
//下面为/etc/rc.d/rc脚本中的一段代码
#First,runtheKILLscripts.
foriin/etc/rc$runlevel.d/K*;do//这是一个for循环,根据前面获取认级别信息,来关闭/etc/rc#.d/下的服务
check_runlevel"$i"||continue

#Checkifthesubsystemisalreadyup.
subsys=${i#/etc/rc$runlevel.d/K??}
[-f/var/lock/subsys/$subsys-o-f/var/lock/subsys/$subsys.init]\
||continue

#Bringthesubsystemdown.
ifLC_ALL=Cegrep-q"^..*init.d/functions"$i;then
$istop//stop关闭服务!
else
action$"Stopping$subsys:"$istop
fi
done

#NowruntheSTARTscripts.
foriin/etc/rc$runlevel.d/S*;do//这也是个for循环,与上面相反,是启动/etc/rc#.d/下面对应的脚本文件
check_runlevel"$i"||continue

#Checkifthesubsystemisalreadyup.
subsys=${i#/etc/rc$runlevel.d/S??}
[-f/var/lock/subsys/$subsys-o-f/var/lock/subsys/$subsys.init]\
&&continue

#Ifwe'reinconfirmationmode,getuserconfirmation
if[-f/var/run/confirm];then
confirm$subsys
test$?=1&&continue
fi

update_boot_stage"$subsys"
#Bringthesubsystemup.
if["$subsys"="halt"-o"$subsys"="reboot"];then
exportLC_ALL=C
exec$istart//启动服务
fi
ifLC_ALL=Cegrep-q"^..*init.d/functions"$i\
||["$subsys"="single"-o"$subsys"="local"];then
$istart//启动服务
else
action$"Starting$subsys:"$istart
fi
done

下图为系统启动时服务开启的界面

wKiom1fTotnyQ5d3AAAo8kiQZsQ648.png

第六步:启动终端

根据前面获取认运行级别来启动终端,如果运行级别为5,则启动图形界面

第七步:用户登录

系统启动流程结束!

问题总结:

在此之前,一直有几点问题困惑着我,我对它们做了一下总结

1)内核文件在磁盘上,系统还没有启动,系统还没有启动,/目录也没有挂载,前面说先找到boot分区,但是boot分区也是在/的目录下,/还没有,去哪找boot!?

问题解答:注意,此时系统去寻找boot分区下的grub菜单、内核文件及rootfs的驱动并不是通过/目录来寻找,因为此时的/还没有挂载,无法找到/下面的boot目录,直接去boot的那个磁盘分区去寻找所需要的文件,具体看一下代码

[root@localhost~]#cat/boot/grub/grub.conf//grub的配置文件
....省略.....
titleCentOS6(2.6.32-642.el6.x86_64)
root(hd0,0)//hd0,0表示的是磁盘分区,此处的意义表示的是将hd0,0先设为/目录,那么下面
的kernel和initrd后面的根"/",表示的也是hd0,0,也就是stage2所在的磁盘分区。避免了通过/目录来寻找
boot分区的难题.
kernel/vmlinuz-2.6.32-642.el6.x86_64roroot=/dev/mapper/vg0-rootrd_NO_LUKSrd_NO_DMLANG=en_US.UTF-8rd_LVM_LV=vg0/swaprd_NO_mdsYSFONT=latarcyrheb-sun16crashkernel=autord_LVM_LV=vg0/rootKEYBOARDTYPE=pcKEYTABLE=usrhgbcrashkernel=autoquietrhgbquiet
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查看/目录的挂在分区
Filesystem1K-blocksUsedAvailableuse%Mountedon
/dev/sda278585525884648156826479%/
/dev/sda1295561167872635146%/boot
tmpfs51193205119320%/dev/shm
[root@localhost~]#fdisk-l//查看磁盘分区信息

disk/dev/sda:10.7GB,10737418240bytes
255heads,63sectors/track,1305cylinders
Units=cylindersof16065*512=8225280bytes

DeviceBootStartEndBlocksIdSystem
/dev/sda1*138305203+83Linux//注意到,起始柱面是从1柱面开始的,根据此前所学内容,第一个柱面是0柱面
/dev/sda2391048811282583Linux
/dev/sda3104913052064352+82Linuxswap/Solaris

3)加载内核后,为避免bug或者人为操作失误,rootfs先以只读方式挂载,只读方式挂载怎么写数据呢?

问题解答:内核在读取到init程序后,其中有一个系统初始化脚本,即/etc/rc.d/rc.sysinit脚本,其中有一段代码如下,在这rootfs会被重新以读写方式挂载。

remount_needed(){
localstateoldifs
["$READONLY"="yes"]&&return1
state=`LC_ALL=Cawk'/\//&&($3!~/rootfs/){print$4}'/proc/mounts`
oldifs=$IFS
IFS=","
foroptin$state;do
if["$opt"="rw"];then
IFS=$oldifs
return1
fi
done
IFS=$oldifs
return0
}

#Remounttherootfilesystemread-write.
update_boot_stageRCmountfs
ifremount_needed;then//根据前面定义的函数,来实现rootfs的读写挂载mount-n-oremount,rw/
action$"Remountingrootfilesysteminread-writemode:"mount-n-oremount,rw/
fi


流程图:

俗话说的好,一图抵千言,我将上面所述的启动流程又画了一幅图,希望以更加清晰地描述CentOS的启动流程。

wKiom1fTj5zC5WgFAAOOI4WkZ3g804.png

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。