linux启动流程(一):BIOS、boot loader与kernel加载

admin 2022年08月28日 1,541次浏览

Linux 的启动流程简单归纳为以下几步:

  • 加载 BIOS 的硬件信息与进行 POST 自检,然后根据设置获取到第一个可启动的设备

  • 读取并执行第一个启动设备内 MBR 的 boot Loader(启动引导程序,现在的 Linux 系统一般使用 grub2)

  • 根据引导程序的设置,加载 Kernel ,Kernel 会开始检测硬件并加载驱动程序

  • 在硬件驱动成功后,Kernel 会主动调用 systemd 程序,并以 default.target 流程启动

    • systemd 执行 sysinit.target 初始化系统及 basic.target 准备操作系统

    • systemd 启动 multi-user.target 下的本机与服务器服务

    • systemd 执行 multi-user.target 下的 /etc/rc.d/rc.local 文件

    • systemd 执行 multi-user.target 下的 getty.target 及登录服务

    • systemd 执行 graphical 需要的服务

1、BIOS、开机自检与MBR(GPT)

要想启动整个操作系统,首先得让系统去加载 BIOS,并通过 BIOS 程序去加载 CMOS 的信息,并且通过 CMOS 内的设置获取到主机的各项硬件配置。在获取到相关硬件信息后,BIOS 将启动 POST 自检程序,然后执行硬件检测的初始化,并设置 PnP 设备,之后再定义出可启动的设备顺序,接下来就会开始进行启动设备的数据读取了

因为系统文件都是放到硬盘中的,所以 BIOS 会指定开机的设备,好让我们可以读取硬盘中的操作系统内核文件。但由于不同的操作系统它的文件系统格式不相同,因此我们必须要以一个启动引导程序来处理内核文件加载的问题,这个启动引导程序就被称为 Boot Loader 。这个 Boot Loader 程序就安装在 MBR 中

2、Boot Loader的功能

由于不同操作系统的文件格式不一致,因此每种操作系统都有自己的 boot loader,用自己的 loader 才能载入核心文件。 其实每个文件系统都会保留一块启动扇区提供操作系统安装 boot loader,而通常操作系统默认都会安装一份 loader 到它根目录所在的文件系统的启动扇区上,boot loader 主要的功能如下:

  • 提供选项:用户可以选择不同的启动选项,这也是多重引导的重要功能

  • 加载核心文件:直接指向可启动的程序区域来启动操作系统

  • 转交其他loader:将启动管理功能转交给其它 loader 负责

由于具有选项功能,因此我们可以选择不同的内核来启动。而由于具有控制权转交的功能,因此我们可以加载其它启动扇区内的 loader

3、加载内核检测硬件与initramfs的功能

当我们借由 boot loader 的管理而开始读取内核文件后,接下来 Linux 就会将内核解压缩到内存当中, 并且利用内核的功能,开始测试与驱动各个周边设备,包括储存设备、CPU、网卡、声卡等等。此时 Linux 内核会以自己的功能重新检测一次硬件,而不一定会使用 BIOS 检测到的硬件信息。也就是说,内核这时才开始接管 BIOS 后的工作。一般内核文件存放路径为 /boot/vmlinuz

[root@localhost ~]# ls --format=single-column -F /boot
# 此版本内核被编译时选择的功能与模块配置文件
config-3.10.0-1160.71.1.el7.x86_64
# 旧版grub1,已经弃用
grub/
# 启动引导程序grub2相关数据目录
grub2/
# 虚拟文件系统,用来做恢复使用
initramfs-0-rescue-10af8a2d3e3840a9a2d13e524a70cdbc.img
# 正常启动使用的虚拟文件
initramfs-3.10.0-1160.71.1.el7.x86_64.img
# 内核文件出现问题时,会使用的虚拟文件
initramfs-3.10.0-1160.71.1.el7.x86_64kdump.img
# 内核功能放置到内存地址的对应表
System.map-3.10.0-1160.71.1.el7.x86_64
# 恢复时用到的内核文件
vmlinuz-0-rescue-10af8a2d3e3840a9a2d13e524a70cdbc*
# 正常启动使用的内核文件
vmlinuz-3.10.0-1160.71.1.el7.x86_64*

需要动态加载的内核模块(可以理解为驱动)将被存放到 /lib/modules/ 中,由于模块放置到磁盘根目录内, 因此在启动的过程中内核必须要挂载根目录,这样才能够读取内核模块提供加载驱动程序的功能。 而且为了担心影响到磁盘内的文件系统,因此启动过程中根目录是以只读的方式来挂载的

/boot/initrd 或 /boot/initramfs 虚拟文件系统也能够通过 boot loader 来加载到内存中,然后这个文件会被解压缩并且在内存当中模拟成一个根目录,而且该模拟在内存当中的文件系统能够提供一个可执行的程序,通过该程序来加载启动过程中所最需要的内核模块, 通常这些模块就是 USB、RAID、LVM、SCSI 等文件系统与磁盘接口的驱动程序。等加载完成后, 会帮助内核重新调用 systemd 来开始后续的正常启动流程

4、systemd与default.target

在内核加载完毕、进行完硬件检测与驱动程序加载后,此时主机硬件就准备就绪了 , 此时内核会主动的调用第一个程序,那就是 systemd,systemd 最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设置、语言设置、文件系统格式及其他服务的启动等。 而所有的操作都会通过 systemd 的默认启动服务集合 /etc/systemd/system/default.target 来规划

4.1、常见target与runlevel的对应

可以作为默认的操作环境 (default.target) 的主要项目有:multi-user.target 与 graphical.target,当然还有某些比较特殊的操作环境,比如 rescue.target,、emergency.target,、shutdown.target、initrd.target 等等。systemd 为了兼容 systemV 的操作行为, 所以也将 runlevel 与操作环境做个结合

[root@localhost ~]#  ll -d /usr/lib/systemd/system/runlevel*.target
/usr/lib/systemd/system/runlevel0.target -> poweroff.target
/usr/lib/systemd/system/runlevel1.target -> rescue.target
/usr/lib/systemd/system/runlevel2.target -> multi-user.target
/usr/lib/systemd/system/runlevel3.target -> multi-user.target
/usr/lib/systemd/system/runlevel4.target -> multi-user.target
/usr/lib/systemd/system/runlevel5.target -> graphical.target
/usr/lib/systemd/system/runlevel6.target -> reboot.target

4.2、systemd的处理流程

当我们取得了 /etc/systemd/system/default.target 这一个默认操作界面的设置后,它会链接到 /usr/lib/systemd/system/ 这个目录下去取得 multi-user.target 或 graphical.target 这两个其中的一,假设我们是使用 graphical.target ,接着下来 systemd 会去找两个地方的设置:

  • /etc/systemd/system/graphical.target.wants/:用户设置加载的 unit

  • /usr/lib/systemd/system/graphical.target.wants/:系统默认加载的 unit

然后再由 /usr/lib/systemd/system/graphical.target 这个配置文件内发现如下的数据:

[root@localhost ~]# cat /usr/lib/systemd/system/graphical.target
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes

这表示 graphical.target 必须要完成 multi-user.target 之后才能够进行,而进行完 graphical.target 之后,还得要启动 display-manager.service 才行

要知道系统的服务启用的流程,可以使用 systemctl list-dependencies graphical.target 命令,如果想要知道背后的配置文件意义, 那就是分别去找出 /etc 与 /usr/lib 底下的 graphical.target.wants/ 目录下的数据就行了,当然,配置文件脚本里面的 Requires 这个设置值所代表的服务,也是需要是先加载的

5、sysinit.target与basic.target

经过上面的流程后,systemd 将执行 sysinit.target 来初始化系统,使用 systemctl list-dependencies sysinit.target我们会看到很多依赖的服务,这些服务可以一个一个去查看设置脚本的内容, 就能够大致理解每个服务的意义。基本上,我们可以将这些服务归类成几个大项:

  • 特殊文件系统装置的挂载:包括 dev-hugepages.mount、dev-mqueue.mount 等挂载服务,主要在挂载跟内存分页使用与消息队列的功能。 挂载成功后,会在 /dev 下面建立 /dev/hugepages/、/dev/mqueue/ 等目录

  • 特殊文件系统的启用:包括磁盘阵列、网络驱动器 (iscsi)、LVM 文件系统、文件系统对照服务 (multipath) 等等

  • 启动过程的信息传递与动画执行:使用 plymouthd 服务搭配 plymouth 命令来传递动画与信息

  • 日志式登录文件的使用:就是 systemd-journald 这个服务的启用

  • 加载额外的内核模块:通过 /etc/modules-load.d/*.conf 文件的设置,让内核额外加载管理员所需要的内核模块

  • 加载额外的内核参数设定:包括 /etc/sysctl.conf 以及 /etc/sysctl.d/*.conf 内部设置

  • 启动系统的随机数生成器:随机数生成器可以帮助系统进行一些密码加密演算的功能

  • 设定终端 (console) 字体

  • 启动动态设备管理器:就是 udevd 这个程序,用在动态对应实际设备存取与设备文件名对应的一个服务

执行完 sysinit.target 之后,就该执行 basic.target 了。sysinit.target 在初始化系统,而 basic .target 则是一个最普通的操作系统。 这个 basic.target 的阶段主要启动的服务大概有这些:

  • 加载 alsa 音效驱动程序:alsa 是一个音效相关的驱动程序,会让系统产生音效

  • 载入 firewalld 防火墙:CentOS 7.x 以后使用 firewalld 取代 iptables 的防火墙设置

  • 加载 CPU 的微指令功能

  • 启动与设置 SELinux 的安全本文:如果由 disable 的状态改成 enable 的状态,或者是管理员设置强制重新设置一次 SELinux 的安全本文,就是在这个阶段处理

  • 将目前的启动过程所产生的启动信息写入到 /var/log/dmesg 当中

  • 由 /etc/sysconfig/modules/*.modules 及 /etc/rc.modules 加载管理员指定的模块

  • 加载 systemd 支持的 timer 功能

在这个阶段完成之后,系统就可以顺利的运行了,就差登录服务、网络服务、本机认证服务等等的 service 类别了,于是就可以进入下个服务启动的阶段了

6、systemd启动multi-user.target下的服务

在加载内核驱动硬件后,经过 sysinit.target 的初始化流程让系统可以读写之后,加上 basic.target 让系统成为操作系统的基础,之后就是服务器要顺利运行时,需要的各种主机服务以及提供服务器功能的网络服务的启动。这些服务的启动则大多是附属于 multi-user.target 这个操作环境下,可以到 /etc/systemd/system/multi-user.target.wants/ 里查看默认要被启动的服务。一般来说服务的启动脚本设置都是放在以下目录中:

  • /usr/lib/systemd/system (系统默认的服务启动脚本设置)

  • /etc/systemd/system (管理员自己开发与设置的脚本设置)

而用户针对主机的本地服务与服务器网络服务的各项 unit 若要 enable 的话,就是将它放到 /etc/systemd/system/multi-user.target.wants/ 这个目录下面,然后做个链接,这样就可以在开机的时候去启动它

6.1、兼容systemV的rc-local.service

当系统完成开机后,如果想要让系统额外执行某些程序的话,可以将该程序命令或脚本的绝对路径名称写入到 /etc/rc.d/rc.local 这个文件里面,在 systemd 中,建议直接写一个 systemd 的启动脚本配置文件到 /etc/systemd/system 下,然后使用 systemctl enable 的方式来设置启用,而不要直接使用 rc.local 这个文件

如果还是非得使用 rc.local,那么可以使用 rc-local.service 这个服务,这个服务不需要启动,它会自己判断 /etc/rc.d/rc.local 是否具有可执行的权限来判断要不要启动这个服务

# 默认rc-local.service服务是没有执行权限的
[root@localhost ~]# ll /etc/rc.d/rc.local
-rw-r--r-- 1 root root 473 1月  14 2022 /etc/rc.d/rc.local
[root@localhost ~]# systemctl status rc-local.service
● rc-local.service - /etc/rc.d/rc.local Compatibility
   Loaded: loaded (/usr/lib/systemd/system/rc-local.service; static; vendor preset: disabled)
   Active: inactive (dead)
# multi-user.target里面也没有这个服务
[root@localhost ~]# systemctl list-dependencies multi-user.target | grep rc-local

# 给rc-local.service添加执行权限
[root@localhost ~]# chmod a+x /etc/rc.d/rc.local
[root@localhost ~]# ll /etc/rc.d/rc.local
-rwxr-xr-x 1 root root 473 1月  14 2022 /etc/rc.d/rc.local
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl status rc-local.service
● rc-local.service - /etc/rc.d/rc.local Compatibility
   Loaded: loaded (/usr/lib/systemd/system/rc-local.service; static; vendor preset: disabled)
   Active: active (exited) since 日 2022-08-28 20:12:02 CST; 2s ago
  Process: 6596 ExecStart=/etc/rc.d/rc.local start (code=exited, status=0/SUCCESS)
8月 28 20:12:02 localhost.localdomain systemd[1]: Starting /etc/rc.d/rc.local Compatibility...
8月 28 20:12:02 localhost.localdomain systemd[1]: Started /etc/rc.d/rc.local Compatibility.
[root@localhost ~]# systemctl list-dependencies multi-user.target | grep rc-local
● ├─rc-local.service

给 rc-local.service 添加执行权限后,脚本就可以放在 /etc/rc.d/rc.local 这个文件里面,系统在每次开机都会去执行这个文件里面的命令了

6.2、提供tty界面与登录服务

在 multi-user.target 下面有个 getty.target 的操作界面选项,这个就是 tty 的登录界面,所以能不能提供适当的登录服务也是 multi-user.target 里面的内容,包括 systemd-logind.service,、systemd-user-sessions.service 等服务

7、 systemd启动graphical.target下的服务

如果 default.target 是 multi-user.target 的话,那么这个步骤就不会进行。如果是 graphical.target,那么 systemd 就会开始加载用户管理服务与图形界面管理程序等,启动图形界面来让用户以图形界面登录系统。可以使用 systemctl list-dependencies graphical.target 来查看 graphical.target 启动的相关服务

到此为止,systemd 就已经完整的处理完毕,可以使用图形界面或命令界面的方式来登入系统,系统也顺利的启动完毕,也能够将写入到 /etc/rc.d/rc.local 的脚本实际执行一次了

8、启动过程会使用到的主要配置文件

8.1、关于模块

主要使用大的配置文件为:/etc/modprobe.d/*.conf 和 /etc/modules-load.d/*.conf,

  • /etc/modules-load.d/*.conf:单纯要内核加载模块的位置

  • /etc/modprobe.d/*.conf:可以加上模块参数的位置

基本上 systemd 已经帮我们将开机会用到的驱动程序全部加载了,只有需要对某些特定的参数要处理时,应该就得要在这里进行了,设置完模块后,需要使用 systemctl restart systemd-modules-load.service 命令,重新加载模块

8.2、/etc/sysconfig/*

  • authconfig

    这个文件主要在规范使用者的身份认证的机制,包括是否使用本机的 /etc/passwd, /etc/shadow 等, 以及 /etc/shadow 密码记录使用何种加密算法,还有是否使用外部密码服务器提供的账号验证 (NIS, LDAP) 等。

    系统默认使用 SHA512 加密算法,并且不使用外部的身份验证机制,不建议手动修改这个文件,应该使用authconfig-tui 命令来修改

  • cpupower

    如果启动 cpupower.service 服务时,就会读取这个配置文件。主要是 Linux 内核如何操作 CPU 的原则。 一般来说,启动 cpupower.service 之后,系统会让 CPU 以最大性能的方式来运行,否则默认就是用多少算多少的模式来处理的

  • firewalld、 iptables-config、 iptables-config、ebtables-config

    与防火墙服务的启动外带的参数有关

  • network-scripts/

    网卡的相关配置文件