Skip to the content.

系统启动其实很复杂,要想每个过程都了解感觉还是很难的。

这里记录一下主要过程吧,将来水平提升了再来补充。

0. 概述

总的来说分成下面几步吧:

有几种常见的组合:

- BIOS legacy & MBR & grub legacy & sysvinit  & initrd.img   --> Centos5
- BIOS legacy & MBR & grub legacy & upstart   & initramfs    --> Centos6
- UEFI        & GPT & grub legacy & upstart   & initramfs    --> Centos6
- BIOS legacy & MBR & grub2       & systemd   & initramfs    --> Centos7
- BIOS legacy & GPT & grub2       & systemd   & initramfs    --> Centos7   # 可选
- UEFI        & GPT & grub2       & systemd   & initramfs    --> Centos7

1. 按下电源

在没按下电源之前,在机器已经通电的情况下,已经有一个bmc模块已经在运行了。通过bmc可以模拟开机的动作,也可以查看机器的状况。

在按下电源的时候,就会进入开机自检的状态,检查设备的CPU、硬盘等,如果发现严重的硬件故障,就会停止启动。

2. BIOS

BIOS有两种模式:

创建的组合是:传统BIOS和MBR,传统BIOS和GPT,UEFI固件和GPT。

通常启动到一定时候就会出现 Press F1 xxxx, F12 xxxxx的字样,选择怎样的启动方式:

一般对硬盘来说,传统BIOS模式和原生UEFI固件启动时候的区别在于:

因为硬盘的第一个扇区太小了,根本干不了啥,所以只能让446字节的这段代码在指向另外一个位置(如果是MBR硬盘的话,就会指向MBR和第一个分区之间的缝隙区域,好尴尬的设置),让它来识别/boot分区,启动GRUB程序;这也是UEFI模式出现的原因之一,它让启动更加的简单了。

3. BootLoader

不管是硬盘还是光驱,都需要BootLoader来启动。

这里就说硬盘了,硬盘不同的分区,还是有点区别的,mbr不支持2TB以上的磁盘,因此,现在GPT分区的硬盘也越来越多了。

3.1 mbr BootLoader

第一个扇区,512字节中的前446个字节就是Boot程序,是一段汇编代码,因为空间只有446个字节,太少了,它的基本思想就是指向其他位置。


# 这三个是硬盘上的位置是系统启动的关键位置。
mbr --->  mbr gap ---> /boot

# 1.mbr 主引导分区,512字节的第一扇区
# 2.mbr gap  主引导分区和第一个分区之间的空隙
# 3./boot分区,一般是第一个分区

使用grub工具可以简单的部署上面说的这些东西,其实就是一些文件。但是使用grubgrub2有点区别,需要注意一下。

3.1.1 grub

grub,一般指的是grub legacy,版本号为0.97Centos6以及之前的版本使用。

grub把启动需要的阶段分成了3个步骤,stage1stage1_5stage2。简单的来说,

其实三个阶段设计的文件都保存在/boot下,当指定setup命令安装grub的时候,会将相应的文件写到指定的位置上。

#大概步骤就是这样了:

stage1 ---> stage1_5 ---> stage2    ---> vmlinuz&initrd ---> ...
启动   ---> fs驱动   ---> grub配置  ---> 内核&更多驱动  ---> ...

MBR --> stage1_5( mbr gap ) --> /boot/grub/menu.lst --> vmlinuz --> ...

这个步骤是固定的,基本需要调整的就是grub的配置文件/boot/grub/menu.lst

3.1.2 grub2

grub 2.x的版本,RHEL7/Centos7以后开始使用,替换了原先的grub legacy

grub2的方式基本和grub差不多,毕竟一脉相承,但是不再使用stage文件,而是用boot.img,core.img代替。

所以启动的步骤也基本类似的:
1,读取硬盘第一扇区,也就是加载了boot.img
2,跳转到mbr和第一个分区的缝隙中的core.img,加载必要的模块,启动grub2
3,跳转到/boot下,grub2启动并加载grub2.cfg的配置文件
4,加载vmlinuz内核等...

简单来说就是: BIOS --> MBR --> core.img ( mbr gap ) --> grub.cfg --> vmlinuz --> ...

同样的,常见的调整就是修改grub2的配置,不过grub2的配置文件相对复杂一些,一般不再直接修改/boot/grub2/grub.cfg文件,而是通过修改/etc/grub/defaultgrub-mkconfig来自动生成配置。

3.2 GPT BootLoader

对于GPT硬盘,一般会使用UEFI进行启动,有时候也会使用BIOS legacy方式。

3.2.1 grub

Centos6系统下的分区如下:

[root@cn4 ~]# df -hT
Filesystem           Type   Size  Used Avail Use% Mounted on
/dev/mapper/vg_cn4-lv_root
                     ext4    17G  675M   16G   5% /
tmpfs                tmpfs  490M     0  490M   0% /dev/shm
/dev/sda2            ext4   477M   27M  425M   6% /boot
/dev/sda1            vfat   200M  264K  200M   1% /boot/efi

启动过程中UEFI直接指向了/boot/efi这个文件系统中的grub.efi文件。

[root@cn4 ~]# cd /boot/efi/EFI/redhat/
[root@cn4 redhat]# ls -l
total 256
-rwx------ 1 root root    880 Aug 28 23:17 grub.conf    # 配置文件
-rwx------ 1 root root 254317 Mar 23  2017 grub.efi     # 加载grub,指向grub.cfg

启动顺序:

UEFI --> grub.efi --> grub.cfg --> vmlinuz --> ...

3.2.2 grub2

BIOS&GPT硬盘下的grub2mbr方式只有一点区别,就是core.img的位置,它放在了一个专用的分区之中,使用不同工具划分分区,需要添加不同的标签,这个分区大小1M即可。

# parted   -->  bios_grub
# gdisk    -->  ef02
# anconda图形界面  --> 需要指定为 BIOS Boot 的Filesystem

使用下面的命令可以修改一个分区为bios_grub模式

parted /dev/sda set 1 bios_grub on

启动顺序为:

BIOS --> MBR --> core.img (EF02 partition) --> grub.cfg --> vmlinuz --> ...

下图显示了GRUB2在MBR和GPT两种格式下的不同之处,来之wikipedia

在UEFI&GPT的模式下,因为UEFI能够直接读取文件系统,不需要直接读取某个扇区中的内容,因此直接指向类似core.img的一个grub.efi文件来启动grub2程序,加载grub.cfg这个配置文件。

这个文件的位置是:/boot/efi/EFI/centos/grubx64.efi

这种模式下的分区也有些区别,需要额外增加一个专用efi分区,格式为fat32,挂载位置是/boot/efi,大小自定默认200M。

# 这种模式下的分区,是这样的:
[root@cn2 ~]# df -hT |grep -v tmpfs
Filesystem              Type      Size  Used Avail Use% Mounted on
/dev/mapper/centos-root xfs        17G  1.2G   16G   7% /
/dev/sda2               xfs      1014M  138M  877M  14% /boot
/dev/sda1               vfat      200M   12M  189M   6% /boot/efi

启动顺序:

启动顺序为:UEFI --> grubx64.efi --> grub.cfg --> vmlinuz --> ...

4. kernel & initrd

现在grub或者grub2的工作就完成了,把内核vmlinuz加载到了内存,并传递了执行的参数以及initrd的位置,就是grub.cfg参数中所写的那些。

vmlinuz&initrd —> 0# —> 1# —> 2# —> …

4.1 vmlinuz

vmlinuz文件是一个bzImage压缩文件,

# 这是centos7.6的vmlinuz文件属性和文件大小
[root@cn2 boot]# file vmlinuz-3.10.0-957.el7.x86_64
vmlinuz-3.10.0-957.el7.x86_64: Linux kernel x86 boot executable bzImage, version 3.10.0-957.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) #1 S, RO-rootFS, swap_dev 0x6, Normal VGA
[root@cn2 boot]# du -h vmlinuz-3.10.0-957.el7.x86_64
6.4M    vmlinuz-3.10.0-957.el7.x86_64

运行后创建0号进程,然后创建1号和2号进程。

# 这是CentOS7.6 的1号和2号进程
[root@cn2 boot]# ps -ef |head -n 3
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 22:03 ?        00:00:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root          2      0  0 22:03 ?        00:00:00 [kthreadd]

但是呢,有一个前提,1号进程执行的程序放在了/usr目录下面(或者说是根目录下面),那么就要求vmlinuz内核必须要先加载根文件系统,为了应对不同类型的根文件系统内核就会越来越大,因此采用了另外一种折中方案,附带一个initrd来放驱动之类的系统专有文件。vmlinuz中只包含一些必要的共性文件。

4.2 initrd.img & initramfs

initrd.img适用于centos5,initramfs适用于centos6和centos7。目的是一样的,但是实际上有很大的区别。

4.2.1 initrd.img

initrd是一个镜像文件系统,被加载到内存中,实现找到真正的根文件系统、加载必要的驱动、收集内核运行状态、设备信息等。其中/sys,/proc,/dev等目录中的信息就是它收集的。另外,完成这些工作后,它将会切换到真正的根文件系统中,将权限正式交给用户空间。

# centos 5.8 中的initrd 文件
[root@localhost ~]# cp /boot/initrd-2.6.18-308.el5.img /tmp/initrd.gz
[root@localhost tmp]# gunzip initrd.gz
[root@localhost tmp]# cpio -id < initrd 

[root@localhost tmp]# ls
bin  dev  etc  init  initrd  lib  proc  sbin  sys  sysroot

4.2.2 initramfs

initramfs是initrd的改进版本,它将进程为1的init文件直接放在了initramfs中,先运行init,再挂载根目录。解压后就可以直接看到init执行文件。如果是centos7版本,可以发现init是一个软连接,目标就是systemd命令。

4.3 sysvinit & upstart & systemd

sysvinit ---> centos5
upstart ---> centos6
systemd ---> centos7 ...

4.3.1 sysvinit

sysvinit启动风格,特点是:单线程的,顺序执行,后面的进程启动必须等待前面进程;同时,不是基于事件的启动方式,在centos5中使用。

它的管理方式就是运行级别体系,运行级别1至6共6个级别,不同的级别使用不同的rc目录中的脚本来启动或者停止相关服务。

4.3.2 upstart

sysvinit的替代品,更优。并行的,只要事件发生,就能够启动服务。比如,光驱或USB设备的的自动识别。其操作和配置方法与sysvinit风格一致。

# 启停服务
service sshd start|stop|restart|status
# 开机自启动
chkconfig --list sshd
chkconfig sshd on|off
# 通过运行级别可以关机
init 0 
# 重启
init 6

4.3.3 systemd

更好更加完善的启动风格,但是及其庞大和复杂,也经常被人诟病。

详细内容可以参看systemd 中文手册

# 启停服务
systemctl start|stop|status|restart sshd.service
# 开机自启动
systemctl disable|enable sshd
# 关机和重启
systemctl poweroff   
systemctl reboot
# 照顾大家习惯沿用了 init 0 和init 6

# 日志的管理
journalctl -f -u sshd
journalctl -u sshd

关于网络服务,centos7里也保留了network服务,并且是sysvinit风格的。未来版本将会取消(RHEL8中已经取消了),使用NetworkManager.service来管理。

# 可以使用原有的风格来管理网络服务
[root@cn2 ~]# chkconfig --list |grep network   # 不确定是否失效
network         0:off   1:off   2:on    3:on    4:on    5:on    6:off
[root@cn2 ~]# service network restart
Restarting network (via systemctl):                        [  OK  ]

# 用systemctl管理的network服务也是调用了rc脚本
[root@cn2 ~]# systemctl cat network
...
ExecStart=/etc/rc.d/init.d/network start
ExecStop=/etc/rc.d/init.d/network stop

systemd 启动过程,还是相当比较复杂的。

x 参考资料