本文译者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布。
本文译者十分愿意与他人分享劳动成果,如果你对我的其他翻译作品或者技术文章有兴趣,可以在如下位置查看现有的作品集:
由于译者水平有限,因此不能保证译文内容准确无误。如果你发现了译文中的错误(哪怕是错别字也好),请来信指出,任何提高译文质量的建议我都将虚心接纳。
bootup — 系统启动流程
在系统启动过程中要涉及多个不同的组件。 按下电源按钮后, 首先BIOS/UEFI做最基本的硬件自检与初始化, 然后加载预设/手动选择的磁盘/网络上的引导加载器(例如GRUB2), 引导加载器进一步从磁盘/网络上加载操作系统内核(例如Linux)。 对于Linux来说, 内核将会(可选的)解压一个initrd(initial RAM disk)镜像(可以用 dracut(8) 之类的工具生成),并执行由"rdinit="内核引导选项指定的init程序(例如 systemd(1)) 以寻找并挂载根文件系统。 完成根文件系统的挂载之后,内核启动由"init="内核引导选项指定的init程序(例如 systemd(1)) 以接管系统的控制权。 该init程序将会负责 检测所有其他的硬件设备、挂载必要的文件系统、启动所有必要的服务,等等。
关机时, init程序将会停止所有服务、 卸载所有文件系统、 (可选的)返回initrd环境卸载根文件系统, 最后关闭电源。
其他有关系统启动流程的信息 可以参考 boot(7) 手册。
当成功挂载了"root="内核引导选项指定的根文件系统之后,内核将启动由"init="内核引导选项指定的init程序, 从这个时间点开始,即进入了"常规启动流程": 检测硬件设备并加载驱动、挂载必要的文件系统、启动所有必要的服务,等等。对于 systemd(1) 系统来说,上述"init程序"就是 systemd 进程, 而整个"常规启动流程"也以几个特殊的 target 单元(详见 systemd.target(5)) 作为节点,被划分为几个阶段性步骤。 在每个阶段性步骤内部,任务是高度并行的, 所以无法准确预测同一阶段内单元的先后顺序, 但是不同阶段之间的先后顺序总是固定的。
当启动系统时,
systemd 将会以 default.target
为启动目标,
借助单元之间环环相扣的依赖关系,即可完成"常规启动流程"。
default.target
通常只是一个指向
graphical.target
(图形界面) 或
multi-user.target
(文本控制台) 的软连接。
为了强制启动流程的规范性以及提高单元的并行性,
预先定义了一些具有特定含义的 target 单元。
详见
systemd.special(7) 手册。
下面的图表解释了 这些具有特定含义的 target 单元之间的依赖关系 以及各自在启动流程中的位置。 图中的箭头表示了单元之间的依赖关系与先后顺序, 整个图表按照自上而下的时间顺序执行。
local-fs-pre.target | v (各个 mounts 与 (各个 swap (各个加密块设备 fsck services……) devices……) devices……) (各个底层服务: (各个底层虚拟 | | | udevd, tmpfiles, 文件系统 mounts: v v v random seed, mqueue, configfs, local-fs.target swap.target cryptsetup.target sysctl, ……) debugfs, ……) | | | | | \__________________|_________________ | ___________________|____________________/ \|/ v sysinit.target | ____________________________________/|\________________________________________ / | | | \ | | | | | v v | v v (各个 (各个 | (各个 rescue.service timers……) paths……) | sockets……) | | | | | v v v | v rescue.target timers.target paths.target | sockets.target . | | | . \_________________ | ___________________/ .................................... \|/ v basic.target | ____________________________________/| emergency.service / | | | | | | v v v v emergency.target display- (图形界面所必需 (各个系统服务) manager.service 的各个系统服务) | | | | | | v | | multi-user.target | | | \_________________ | _________________/ \|/ v graphical.target
用斜体标识的目标单元经常被用作启动目标。
有两种方法可以指定启动目标:
(1)使用
systemd.unit=
内核引导选项(参见
systemd(1));
(2)使用 default.target
软连接。
因为 timers.target
以异步方式包含在
basic.target
中,
所以 timer 单元可以依赖于在 basic.target
之后才启动的服务。
在initrd内部, 也可以将 systemd 用作 init程序(由"rdinit="内核引导选项指定)。
此时
initrd.target
将是默认目标。
initrd内部启动流程的上半部分与前一小节
basic.target
之前的部分完全相同。
随后的启动流程将以 initrd.target
为目标(如下图所示)。
在挂载任何文件系统之前(也就是在启动 local-fs-pre.target
之前),
systemd-hibernate-resume@.service
将会首先完成启动,
以检查系统是要从先前的休眠状态中恢复,
还是要执行常规的启动流程。
在检查完成之前,将不会挂载任何文件系统。
当根文件系统设备可用时,将到达
initd-root-device.target
目标。
如果成功的将根文件系统挂载到
/sysroot
目录,那么
sysroot.mount
单元将被启动,然后进一步到达
initrd-root-fs.target
目标。
initrd-parse-etc.service
将会分析
/sysroot/etc/fstab
文件以挂载
/usr
(若需要)
与带有 x-initrd.mount 标记的挂载点。
所有这些挂载点都将被挂载到 /sysroot
之下,然后流程到达
initrd-fs.target
目标。再接下来
initrd-cleanup.service
将会使用 systemctl --no-block isolate 命令启动
initrd-switch-root.target
目标。
因为 isolate 表示立即停止所有在新的目标单元中不需要的进程,
所以此动作实际上是为接下来切换根目录做预先的准备(也就是清理环境)。
最后,启动 initrd-switch-root.service
服务,将系统的根目录切换至
/sysroot
目录。
(之前的流程与上一小节完全相同)
:
v
basic.target
| emergency.service
______________________/| |
/ | v
| initrd-root-device.target emergency.target
| |
| v
| sysroot.mount
| |
| v
| initrd-root-fs.target
| |
| v
v initrd-parse-etc.service
(各个自定义的 |
initrd services……) v
| (sysroot-usr.mount 以及
| fstab 中带有 x-initrd.mount
| 标记的各个挂载点)
| |
| v
| initrd-fs.target
\______________________ |
\|
v
initrd.target
|
v
initrd-cleanup.service
(使用 isolates 启动 initrd-switch-root.target)
|
v
______________________/|
/ v
| initrd-udevadm-cleanup-db.service
v |
(各个自定义的 |
initrd services……) |
\______________________ |
\|
v
initrd-switch-root.target
|
v
initrd-switch-root.service
|
v
切换到主机上的操作系统
systemd 系统在关机时同样遵循固定的流程, 具体如下图所示:
(与所有系统服务互斥) (与所有文件系统 mounts, swaps, cryptsetup devices 互斥) | | v v shutdown.target umount.target | | \____________________________________ ______/ \ / | v (各个底层 services) | | v final.target | | _____________________________________/ \_________________________________ / | | \ | | | | v v v v systemd-reboot.service systemd-poweroff.service systemd-halt.service systemd-kexec.service | | | | v v v v reboot.target poweroff.target halt.target kexec.target
用斜体标识的目标单元经常被用作关机目标。
注意,
systemd-halt.service(8),
systemd-reboot.service
, systemd-poweroff.service
,
systemd-kexec.service
会将系统与 systemd(PID=1)
带入关机流程的第二个阶段(由 systemd-shutdown
执行),
也就是不再考虑任何服务与单元等概念,
只用一种简单粗暴的方式卸载所有文件系统、杀死所有进程、释放所有资源。
一般来说,在第一阶段结束时,常规应用都已终止、常规资源都已释放,第二阶段只是一个兜底的安全网,
那些在第一阶段基于单元的关闭流程中(见前文)
未能结束的进程将被强制终止、未能释放的资源将被强制释放。