Linux 中引导与启动流程
BIOS 启动流程
当前,主机系统在载入硬件驱动方面的程序有两种:使用 BIOS 和 UEFI。简单的启动流程可以分为以下步骤:
- 载入 BIOS 或 UEFI 的硬件信息与自检(POST,Power-On Self-Test),并根据设置选择第一个可启动的设备。
- 对于 BIOS,通过硬件的 INT 13 中断功能,读取并执行第一个可启动设备内 MBR 的引导加载程序(Boot Loader);对于 UEFI,读取并执行第一个可启动设备内 EFI 分区的引导加载程序。
- 根据引导加载程序的设置,识别文件系统格式,并加载核心文件。内核开始检测硬件与驱动程序。
- 启动初始进程管理程序(systemd 或 System V),最终目的是启动基础服务和用户设置的启动程序。
在使用 BIOS 时,通常搭配 MBR(Master Boot Record)分区表。而在使用 UEFI 时,通常搭配 GPT(GUID Partition Table)分区表。GPT 格式的启动过程与 MBR 类似,不同之处在于开机管理程序是否支持 GPT。
当引导加载程序读取到内核文件后,Linux 将内核解压缩到内存中,并开始测试和驱动硬件。此时,Linux 内核将以自己的检测结果为准,而不考虑 BIOS 的硬件检测结果。也就是说,内核此时开始接管 BIOS 后的工作。
多系统引导
引导加载程序除了可以安装在 MBR 外,还可以安装到每个文件系统的引导扇区(Boot Sector)。
例如,硬盘第一和第二分区分别安装了 Windows 和 Linux。开机装载第一个分区引导加载程序,此时会出现分支选项:
- 选择启动 Windows,直接加载第一个分区中的 Windows 核心文件。
- 选择启动 Linux,则转到第二个分区的引导扇区,由第二个分区的引导加载程序完成 Linux 系统启动工作。
如果要安装多重开机,一般先安装 Windows 再安装 Linux,因为 Windows 安装时会直接覆盖掉 MBR 以及自己所在分区的引导扇区,并且 Windows 的引导程序默认没有转移引导功能。而 Linux 安装时可以选择引导加载程序不安装到 MBR,只装在指定分区的引导扇区。之后修改引导程序加入 Windows 开机选项,完成双系统引导功能。
引导目录说明
如果使用 GRUB 引导,需要在 /boot
分区中存放开机过程所需的文件:
[root@101c7 ~]$ ll /boot
total 149232
-rw-r--r--. 1 root root 153596 Aug 31 10:57 config-3.10.0-1160.41.1.el7.x86_64
-rw-r--r--. 1 root root 147819 Apr 20 2018 config-3.10.0-862.el7.x86_64
drwxr-xr-x. 3 root root 17 Sep 7 13:34 efi
drwxr-xr-x. 2 root root 27 Sep 7 13:35 grub
drwx------. 5 root root 132 Sep 7 05:55 grub2
-rw-------. 1 root root 55385253 Sep 7 13:37 initramfs-0-rescue-77a36143eb014dd5a0d6e738b1d84778.img
-rw-------. 1 root root 21322754 Sep 7 05:55 initramfs-3.10.0-1160.41.1.el7.x86_64.img
-rw-------. 1 root root 13851614 Sep 18 23:13 initramfs-3.10.0-1160.41.1.el7.x86_64kdump.img
-rw-------. 1 root root 21288233 Sep 7 05:56 initramfs-3.10.0-862.el7.x86_64.img
-rw-------. 1 root root 13761710 Sep 9 15:00 initramfs-3.10.0-862.el7.x86_64kdump.img
-rw-r--r--. 1 root root 320757 Aug 31 10:57 symvers-3.10.0-1160.41.1.el7.x86_64.gz
-rw-r--r--. 1 root root 304926 Apr 20 2018 symvers-3.10.0-862.el7.x86_64.gz
-rw-------. 1 root root 3620596 Aug 31 10:57 System.map-3.10.0-1160.41.1.el7.x86_64
-rw-------. 1 root root 3409143 Apr 20 2018 System.map-3.10.0-862.el7.x86_64
-rwxr-xr-x. 1 root root 6224704 Sep 7 13:37 vmlinuz-0-rescue-77a36143eb014dd5a0d6e738b1d84778
-rwxr-xr-x. 1 root root 6773352 Aug 31 10:57 vmlinuz-3.10.0-1160.41.1.el7.x86_64
-rwxr-xr-x. 1 root root 6224704 Apr 20 2018 vmlinuz-3.10.0-862.el7.x86_64
/boot
内文件与目录说明如下所示:
文件 | 说明 |
---|---|
config-* | 此版本内核被编译时选择的功能与模块配置文件。 |
grub、grub2 | 开机管理程序 grub 相关数据目录。 |
initramfs-0-rescue-* | 救援模式的虚拟文件系统。 |
initramfs-*.img | 正常模式的虚拟文件系统。 |
initramfs-*kdump.img | 内核出问题时用到的虚拟文件系统。 |
System.map-* | 内核功能放置到内存地址的对应表。 |
vmlinuz-0-rescue-* | 救援模式的内核文件。 |
vmlinuz-* | 正常模式的内核文件。 |
虚拟文件系统
Linux 内核可以动态载入内核模块(驱动),这些内核模块放置在 /lib/modules/
目录内。由于模块放置到根目录内,因此开机过程中内核必须要以只读方式挂载根目录,才可以读取到驱动程序。
但是在挂载根目录时,很大可能 Linux 内核并不认识 SATA 设备。为了让内核顺利执行下去,引用了一个虚拟文件系统(Initial RAM Disk 或 Initial RAM Filesystem)来处理,就是在 /boot
目录内以 initramfs 开头命名的 img 文件。
虚拟文件系统的 img 文件也能被引导加载程序载入到内存中,解压后在内存中仿真成一个根目录。内核会读取虚拟文件系统内的驱动,通常是与磁盘接口和文件系统格式支持有关的驱动程序。
等驱动载入完毕,根目录所在分区信息被正确识别后,此虚拟文件系统被卸载,并挂载实际的根目录文件系统,继续后续开机流程。
可以使用 lsinitrd
命令查看 initramfs 镜像的内容:
[root@101c7 ~]$ lsinitrd /boot/initramfs-3.10.0-1160.41.1.el7.x86_64.img
Image: /boot/initramfs-3.10.0-1160.41.1.el7.x86_64.img: 21M
========================================================================
Early CPIO image
========================================================================
drwxr-xr-x 3 root root 0 Sep 7 05:55 .
-rw-r--r-- 1 root root 2 Sep 7 05:55 early_cpio
drwxr-xr-x 3 root root 0 Sep 7 05:55 kernel
drwxr-xr-x 3 root root 0 Sep 7 05:55 kernel/x86
drwxr-xr-x 2 root root 0 Sep 7 05:55 kernel/x86/microcode
-rw-r--r-- 1 root root 6476 Sep 7 05:55 kernel/x86/microcode/AuthenticAMD.bin
========================================================================
Version: dracut-033-572.el7
Arguments: -f
dracut modules:
bash
nss-softokn
i18n
microcode_ctl-fw_dir_override
shutdown
========================================================================
drwxr-xr-x 12 root root 0 Sep 7 05:55 .
crw-r--r-- 1 root root 5, 1 Sep 7 05:55 dev/console
crw-r--r-- 1 root root 1, 11 Sep 7 05:55 dev/kmsg
crw-r--r-- 1 root root 1, 3 Sep 7 05:55 dev/null
lrwxrwxrwx 1 root root 7 Sep 7 05:55 bin -> usr/bin
drwxr-xr-x 2 root root 0 Sep 7 05:55 dev
可以看到,这个 img 文件可以分为两部分。前半部分包括一些预先声明的数据,其中包括一个可执行的 bin 文件;而后半部分才是内核会去读取的重要文件。
进一步解压整个 img 文件:
[root@101c7 initrams]$ cp /boot/initramfs-3.10.0-1160.41.1.el7.x86_64.img init.img
[root@101c7 initrams]$ file init.img
init.img: ASCII cpio archive (SVR4 with no CRC)
[root@101c7 initrams]$ /usr/lib/dracut/skipcpio init.img | zcat | cpio -div
init
shutdown
88189 blocks
[root@101c7 initrams]$ ll
total 8
lrwxrwxrwx. 1 root root 7 Sep 19 09:10 bin -> usr/bin
drwxr-xr-x. 2 root root 45 Sep 19 09:10 dev
drwxr-xr-x. 12 root root 4096 Sep 19 09:10 etc
lrwxrwxrwx. 1 root root 23 Sep 19 09:10 init -> usr/lib/systemd/systemd
lrwxrwxrwx. 1 root root 7 Sep 19 09:10 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Sep 19 09:10 lib64 -> usr/lib64
drwxr-xr-x. 2 root root 6 Sep 19 09:10 proc
drwxr-xr-x. 2 root root 6 Sep 19 09:10 root
drwxr-xr-x. 2 root root 6 Sep 19 09:10 run
lrwxrwxrwx. 1 root root 8 Sep 19 09:10 sbin -> usr/sbin
-rwxr-xr-x. 1 root root 3117 Sep 19 09:10 shutdown
drwxr-xr-x. 2 root root 6 Sep 19 09:10 sys
drwxr-xr-x. 2 root root 6 Sep 19 09:10 sysroot
drwxr-xr-x. 2 root root 6 Sep 19 09:10 tmp
drwxr-xr-x. 7 root root 66 Sep 19 09:10 usr
drwxr-xr-x. 2 root root 29 Sep 19 09:10 var
解压后查询下这个文件系统中 systemd 调用的运行模式:
[root@101c7 initrams]$ ll usr/lib/systemd/system/default.target
lrwxrwxrwx. 1 root root 13 Sep 19 09:10 usr/lib/systemd/system/default.target -> initrd.target
[root@101c7 initrams]$ systemctl list-dependencies initrd.target
initrd.target
● ├─dracut-cmdline.service
● ├─dracut-initqueue.service
● ├─dracut-mount.service
● ├─dracut-pre-mount.service
通过查询 initrd.target 可以知道内核先是用载入虚拟文件系统中的 basic.target、sysinit.target 等功能启用的流程,让系统顺利运行,再卸载虚拟文件系统,挂载实际系统根目录。
如果想要创建一个自定义的 initramfs 镜像,可以使用 dracut
或 mkinitrd
来处理。例如,新建一个添加了 x200 网卡驱动和 nfs 文件系统驱动的 initramfs 镜像:
[root@101c7 ~]$ dracut -v -add-drivers "x200" --filesystems "nfs" > initramfs-new.img & (uname -r)
Systemd 启动流程
Systemd 是一种系统和服务管理器,是 Linux 系统中的一项关键技术。它可以用于启动、停止和管理系统进程、网络、文件系统和其他系统资源。Systemd 最初是为 Red Hat Linux 发行版设计的,但现在已被广泛采用,并被大多数 Linux 发行版所使用。
Systemd 的主要优势是速度和可靠性。它的启动速度远远快于传统的 init 系统,因为它能够并行启动多个服务。此外,Systemd 还提供了强大的日志功能和灵活的单元文件格式,可用于管理各种类型的服务和资源。
Systemd 还引入了一些新的概念,例如单元和片段。单元是 Systemd 中的基本单位,用于描述系统资源,如服务、套接字、挂载点等。片段是单元文件的组成部分,可供其他单元文件重用。这种灵活性使得 Systemd 能够更容易地管理和组织系统资源,使其更加模块化和可重用。
启动流程
在内核载入完毕硬件驱动后,采用 Systemd 管理服务的启动流程如下:
- 内核会主动调用第一个程序
systemd
执行 sysinit.target,初始化系统及准备基本系统; - 接下来进入
/usr/lib/systemd/system/
目录,调用 multi-user.target(default.target); - 然后系统会去
/etc/systemd/system/multi-user.target.wants/
目录查找用户设置的开机启动服务(unit); - 最后到
/usr/lib/systemd/system/multi-user.target.wants/
目录查找系统默认的开机启动服务(unit); - (非必需)执行图形化界面所需的服务。
关于最终启动的服务有哪些,可以使用 systemctl list-dependencies
命令查询:
[root@101c7 initrams]$ systemctl list-dependencies multi-user.target | grep target
multi-user.target
● ├─basic.target
● │ ├─selinux-policy-migrate-local-changes@targeted.service
● │ ├─paths.target
● │ ├─slices.target
● │ ├─sockets.target
● │ ├─sysinit.target
● │ │ ├─cryptsetup.target
● │ │ ├─local-fs.target
● │ │ └─swap.target
● │ └─timers.target
● ├─getty.target
● └─remote-fs.target
此外,为了与之前通过 /etc/rc.d/rc.local
配置自启动脚本的方式保持兼容,CentOS 7 引入了一个名为 rc-local.service 的服务来执行这些脚本。
启动相关目录
开机会读取的文件目录如下:
/etc/modules-load.d/*.conf
:配置内核需要加载的模块。/etc/modprobe.d/*.conf
:可以设置模块参数的配置。/etc/sysconfig/
:系统配置目录下有许多环境配置文件,其中一些重要的文件包括:authconfig
:用于规范用户身份认证机制,包括是否使用本地的/etc/passwd
、/etc/shadow
等文件以及密码记录使用的加密算法,是否使用外部密码服务器提供的账号验证(如 NIS、LDAP)等。cpupower
:如果启动了cpupower.service
服务,会读取此配置文件。firewalld
、iptables-config
、iptables-config
、ebtables-config
:与防火墙服务相关的配置。network-scripts
:网卡设置。
System V 启动流程
System V (System 5) 是 Unix 操作系统中的一个初始化系统。它是早期 Unix 系统的标准初始化系统,常见于许多 Linux 发行版中。System V 通过运行一个脚本,逐个启动和停止系统服务来初始化系统。这些脚本通常存储在 /etc/rc.d/init.d/
目录下,每个脚本对应着一个系统服务。在系统启动或关闭时,System V 会根据 /etc/inittab
文件的配置来执行这些脚本。
System V 的缺点在于启动过程比较慢,因为它需要逐个启动和停止每个系统服务,这使得启动时间较长。此外,System V 在处理系统服务时需要手动编写脚本,并且难以处理服务之间的依赖关系,这可能导致系统启动失败或服务无法正常运行。由于这些缺点,现代 Linux 发行版逐渐转向使用 Systemd 作为默认初始化系统。
启动流程
在内核加载完硬件驱动后,内核会主动调用第一个进程 /sbin/init
。它最主要的功能是准备软件执行环境。启动流程如下:
- init 取得默认执行等级,执行
/etc/rc.d/rc.sysinit
文件来准备软件执行的操作环境,例如网络和时区等。 - init 执行 run level 的各个服务的启动(脚本方式)。例如执行等级是 5,则只执行
5:5:wait:/etc/rc.d/rc5
这行:- 和 rc5 相关的脚本存放在
/etc/rc5.d/
下面,以 Kxx 开头的文件代表执行 stop,以 Sxx 开头代表执行 start 操作。 - 这些脚本全部是连接文件,连接到 stand alone 服务启动的目录
/etc/init.d/
内。 - S 或 K 后面两位数字代表执行顺序,数字越小执行越早。
- 和 rc5 相关的脚本存放在
- 设置好组合键功能和不断电系统 pf、pr 机制。
- 执行用户自定义脚本
/etc/rc.d/rc.local
中的程序。 - 启动终端模拟程序
mingetty
,以启动登录进程。 - (非必须)启动图形界面,通过执行
/etc/X11/perfdm -nodaemon
来启动。
启动相关目录
在系统启动过程中,有一些与配置相关的目录和文件:
-
/etc/modprobe.conf
这个文件定义了系统启动时需要加载的模块。模块文件存放在
/etc/sysconfig/modules/
目录下。 -
/etc/sysconfig/*
这个目录下的文件与系统服务的配置相关。其中一些重要的文件包括:
authconfig
:用于设置用户身份认证机制。clock
:用于设置 Linux 主机的时区。i18n
:用于设置语言环境。keyboard
和mouse
:用于设置键盘和鼠标的样式。network
:用于设置网络启用、主机名和网关。network-scripts
:用于设置网卡配置。
init 配置文件
系统的初始化过程通过配置文件 /etc/inittab
进行规划。每一行配置由冒号:
分隔的四个字段组成:
-
设置选项
这是一个最多四个字符的字段,用于说明 init 的主要工作选项。
-
执行等级
这个字段表示在哪些执行等级下运行该选项。例如,35 表示在执行等级 3 和 5 下都会运行。
-
init 的操作行为
可以设置的值包括:
initdefault
:设置默认的执行等级。sysinit
:用于系统初始化的操作选项。ctrlaltdel
:表示组合快捷键 Ctrl+Alt+Del 是否可以重新启动系统。wait
:表示后面设置的命令必须要执行完毕才能继续下面的操作。respawn
:表示后面的命令可以无限重启,例如 tty1。
-
命令选项
这个字段用于指定要执行的命令,通常是一些脚本文件。