一. 启动系统流程简介

系统启动流程


如上图,简述系统启动的大概流程:


1:硬件引导

UEFi或BIOS初始化,运行POST开机自检


2:grub2引导阶段

系统固件会从MBR中读取启动加载器,然后将控制权交给启动加载器GRUB2,GRUB2从/boot/grub2/grub.cfg文件中加载配置并显示一个菜单,在这个菜单中可以选择要启动的内核。


3:内核引导阶段

1. 加载 vmlinuz和initramfs

在选择内核或到达超时时间后,启动加载器会从磁盘加载内核(vmlinuz)和initramfs,并将它们放入内存中,initramfs中包含启动时所有必要硬件的内核模块(驱动)和初始化脚本等


2. 加载硬件驱动并初始化硬件

内核启动并完成初始化后,会执行initramfs中的init初始化程序,寻找硬件的相关驱动并初始化相关硬件。initramfs中主要包含了系统启动过程中依赖硬件的驱动程序以及一些初始化脚本程序。


3. 挂载根文件系统到/sysroot目录

启动/usr/sbin/init(PID=1),现在都是/usr/lib/systemd/systemd的一个软链接,systemd会执行initrd.target包含的所有单元,并将根文件系统挂载到/sysroot/目录, 在initrd.target启动时的依赖单元,会按照/etc/fstab设置对硬盘进行挂载。


4. 根文件系统从initramfs切换到/sysroot,开始用户态阶段初始化程序

内核将根文件系统从initramfs切换为/sysroot(硬盘上的根文件系统),systemd会找到磁盘上安装的systemd并自动重新执行


4:systemd引导阶段

1. 执行systemd,开始用户态的初始化程序

硬盘上安装的systemd会查找从内核命令行传递的目标或是系统中配置的默认目标并启动对应单元后就可以进入到对应的登录界面。默认目标是:/etc/systemd/system/default.target,


2. 最先执行sysinit.target,其次执行basic.target与getty.target准备好系统基本环境。然后启动multi-user.target下的相关应用。

如果默认目标为multi-user.target(字符界面),systemd会先执行sysinit.target初始化系统之后执行basic.target与getty.target准备基本系统环境和终端,再启动multi-user.target下的相关应用,同时执行/etc/rc.d/rc.local(需要执行权限)与与登录服务(systemd-logind.service),开启登录界面


二. 启动单元介绍

启动单元列表


1:内核态阶段

initrd.target:initramfs 阶段的主目标

initrd-root-device.target

确保根存储设备已准备就绪

sysroot.mount

将根文件系统挂载到 /sysroot 目录

initrd-parse-etc.service

解析真实根文件系统中的 /etc 配置

initrd-root-fs.target

表示根文件系统已成功挂载到 /sysroot

sysroot-usr.mount

挂载独立 /usr 分区到 /sysroot/usr

x-initrd.target

自定义初始化目标,通常用于触发特定硬件配置或服

initrd-fs.target

确保所有必要的文件系统已挂载

2:用户态阶段

sysinit.target:系统初始化

local-fs.target

挂载本地文件系统,根据 /etc/fstab 配置完成分区挂载

cryptsetup.target

初始化磁盘加密设备,解密并激活加密的存储设备

systemd-udevd.service

启动 udev 设备管理器,生成设备节点并触发设备相关事件

swap.target

激活交换分区(swap)

systemd-sysctl.service

应用内核参数

systemd-sysusers.service

创建系统用户和组

systemd-tmpfiles-setup.service

创建临时目录(如/tmp、/run)

systemd-journald.service

启动系统日志守护进程,收集内核、用户态服务的日志

basic.target:准备系统的基础系统环境

timers.target

管理定时任务(systemd-timer单元)

paths.target

监控文件 / 目录变化并触发服务

slices.target

资源控制单元,通过CGroup限制服务资源

sockets.target

启动基于 socket 的服务

multi-user.target:并行启动多用户模式下的所有服务

multi-user.target下的所有服务默认并行启动,通过requires after before设置了依赖关系和启动顺序的按设置的顺序和依赖关系启动。


graphical.target:启动图形界面模式下的所有服务


三. 系统启动依赖关系

  
  systemctl list-dependencies
  

系统启动依赖关系


  
  systemd-analyze blame
  

启动服务耗时排序


  
  systemd-analyze critical-chain
  

启动unit顺序和耗时


Linux 内核启动传递参数


四. 相关实战案例

1. 逻辑卷激活服务

注意: 某些发行版系统不会开机自动激活逻辑卷,这就导致基于逻辑卷的文件系统开机无法自动挂载,导致服务异常,这时候就需要配置开机自动激活逻辑卷。

local-fs-pre.target是执行/etc/fstab中的挂载项之前的确认target,systemd-remount-fs.servic是根文件系统的重新挂载服务,内核态阶段根文件系统是以只读模式挂载的,切换到用户态后会通过systemd-remount-fs.servic重新以读写模式挂载。 激活服务需要在systemd-remount-fs.servic之后,因为需要执行磁盘上的逻辑卷激活命令,在local-fs.target之前执行,这样就可以实现在实际挂载/etc/fstab的挂载项的时候逻辑卷已经激活。


[Unit]
Description=Activate LVM Volumes for fstab
After=systemd-remount-fs.service    # 明确依赖根文件系统已挂载为读写模式
Before=local-fs-pre.target          # 在 local-fs-pre.target 之前执行
Requires=systemd-remount-fs.service # 强依赖根文件系统可写状态
DefaultDependencies=no              # 禁用默认依赖链,避免冲突

[Service]
Type=oneshot                        # 根文件系统已挂载为读写模式,路径 /usr/sbin/vgchange 可访问
ExecStart=/usr/sbin/vgchange -ay    # 逻辑卷激活逻辑
ExecStart=/bin/sh -c "type vgchange && timeout 10s vgchange -ay;echo $? >> /root/lvm.log;date -R >> /root/lvm.log;"
RemainAfterExit=yes

[Install]
WantedBy=local-fs-pre.target        # 属于local-fs-pre.target目标单元


2. 最后启动指定服务

保证在所有都启动完成后再最后执行该逻辑,保证系统环境和服务都已完全启动。 仅保证在multi-user.target所有服务启动后启动当前服务,无法保证multi-user.target所有服务启动成功后再启动当前服务,如果需要依赖具体服务成功启动再执行该服务,再增加依赖服务启动成功的判断逻辑。

配置After=multi-user.target和Requires=multi-user.target后,该服务会在所有服务启动后最后执行。ExecStart中再通过shell命令先判断是否满足条件,比如网卡是否启动和ip是否启用,然后再执行具体的逻辑或者启动服务。


[Unit]
Description=Run Final Task After All Critical Services
Requires=multi-user.target           # 核心依赖:确保所有关键系统服务已完成
After=multi-user.target              # 确保顺序:确保在multi-user.target所有服务启动后再执行

[Service]
Type=oneshot
ExecStart=/bin/sh -c "ifconfig eth0 | grep -wiq RUNNING && ifconfig eth0 | grep -wiq inet && echo 'All services are up' > task.log"
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target