LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Ubuntu(2):systemd启动流程

 

systemd是 Linux 系统中最主流的初始化系统(init)和服务管理器,旨在替代传统的 SysV init 系统。它不仅是系统启动的第一个进程(PID 1),还提供了一套完整的系统管理工具集,深度集成到现代 Linux 发行版(如 Ubuntu、Fedora、Debian、RHEL)中。

1 systemd作用

systemd 的核心作用
1. 系统初始化与守护进程管理
- 并行启动:通过依赖关系(DAG 有向无环图)并发启动服务,显著加速系统启动。
- 服务管理:统一管理所有守护进程(服务),支持启动、停止、重启、状态监控。
- 按需激活:通过 套接字激活(Socket Activation)或 总线激活(D-Bus Activation),服务仅在收到请求时启动,节省资源。

2. 系统状态监控与日志
- 状态快照:记录所有服务、挂载点、设备等系统组件的实时状态。
- 集中式日志:通过 journald 收集二进制结构化日志,支持高效检索(如 journalctl -u nginx)。

3. 系统资源管理
- 资源隔离:通过 Linux cgroups 实现:
- 限制服务资源(CPU/内存/磁盘 I/O)。
- 自动生成进程树,确保服务退出时清理所有子进程。
- 安全控制:支持内核特性(如 SELinux、AppArmor、Seccomp)的配置。

4. 系统事件响应
- 设备热插拔:udevd 动态管理设备(如插入 USB 时自动挂载)。
- 用户会话管理:logind 处理用户登录/注销、电源按钮事件(如合盖休眠)。
- 定时任务:替代 cron,通过 .timer 单元实现精确计划任务。

5. 网络与时间管理
- 网络配置:networkd 支持复杂网络(VLAN、VPN、路由等)。
- 时间同步:timedatectl 管理时区,并可与 systemd-timesyncd 集成替代 NTP。

systemd 在功能集成和启动速度上领先,但牺牲了简洁性;替代方案更适合轻量级/容器化场景。下面是类似技术对比:

技术优势劣势适用场景
systemd 统一管理(服务/日志/网络),快速并行启动 体系庞大,违背 Unix "单一职责"哲学 现代 Linux 发行版
SysV init 简单,脚本透明可见 顺序启动慢,依赖管理弱 传统嵌入式系统
OpenRC 轻量级,支持 BSD,配置灵活 无内置日志/网络管理,依赖外部工具 Gentoo/Alpine Linux
runit 极简设计(< 1000 行代码),可靠 功能有限,需组合多个工具 Void Linux/Docker 容器
s6-overlay 容器友好,无 PID 1 依赖 配置复杂,社区生态小 轻量级容器初始化

2 systemd主要组成

systemd 是一个模块化系统,由多个相互协作的组件组成,主要分为以下核心部分:

组件二进制文件功能描述
systemd /usr/lib/systemd/systemd 主进程 (PID 1),管理单元、依赖解析、进程生命周期
journald systemd-journald 日志收集与存储,二进制格式存储日志,支持结构化查询
logind systemd-logind 用户会话管理(登录/注销/挂起),处理电源按钮事件
networkd systemd-networkd 网络配置管理(支持 VLAN/VxLAN/路由等)
resolved systemd-resolved DNS 解析管理,支持 DNSSEC/LLMNR/mDNS
timedated systemd-timedated 系统时间和时区管理
udevd systemd-udevd 设备管理(热插拔事件处理),替换传统 udev
systemctl systemctl 命令行工具,控制单元(服务启停/状态查看)
systemd-analyze systemd-analyze 分析系统启动性能

3 systemd代码解析

执行流程概要

阶段1: 预初始化 (Pre-Init)
1. 环境准备
- 记录内核/用户空间启动时间戳
- 检测是否跳过初始化(反序列化/切换根目录)
- 保存原始环境变量和命令行参数
- 配置日志系统(强制syslog→journal)

2. PID1专属初始化
- 挂载早期文件系统 (/proc, /sys, /dev)
- 加载安全模块 (SELinux/SMACK/AppArmor/IMA)
- 初始化系统时钟和RTC处理
- 配置核心转储参数
- 加载必需内核模块 (kmod)
- 从EFI获取随机种子

阶段2: 配置加载
1. 资源限制
- 保存原始文件描述符/内存锁限制
- 设置默认资源限制 (RLIMIT_NOFILE/RLIMIT_MEMLOCK)

2. 配置解析
- 解析配置文件 (system.conf/user.conf)
- 处理内核命令行参数 (如 systemd.unit=)
- 执行安全策略检查 (root/容器/chroot限制)

阶段3: 运行时初始化
1. 环境设置
- 应用内核参数 (时钟/随机种子)
- 配置核心转储模式
- 收集文件描述符用于状态恢复

2. 系统基础
- 设置控制台终端
- 挂载cgroup控制器
- 配置主机名/机器ID
- 初始化回环网络设备
- 提升系统资源限制 (文件描述符/套接字队列)

阶段4: 服务管理核心
1. 管理器启动
- 创建Manager对象(单元/作业管理中心)
- 设置默认参数 (超时/日志/资源限制)
- 反序列化状态(若通过--deserialize启动)

2. 默认任务
- 加载默认目标 (如 graphical.target)
- 创建启动作业并加入事务队列

阶段5: 事件处理
1. 主循环
- 事件监听 (D-Bus/信号/文件描述符)
- 处理单元队列 (启动/停止服务)
- 依赖解析和事务处理

阶段6: 退出处理
1. 状态迁移
- 正常退出:返回状态码
- 系统操作:重启/关机/切换根目录
- 崩溃处理:核心转储/应急Shell

2. 控制权移交
- 调用systemd-shutdown执行关机操作
- 容器环境:退出并返回错误码
- 物理机:冻结或强制重启

main
├── redirect_telinit -- 如果SysV兼容:若以"init"名称调用,重定向到systemctl
├── dual_timestamp_from_monotonic -- 记录内核启动时间戳
├── dual_timestamp_get -- 记录用户空间启动时间戳
├── early_skip_setup_check -- 检测是否跳过初始化(--deserialize/--switched-root参数)
├── save_argc_argv -- 保存原始命令行参数
├── save_env -- 保存原始环境变量
├── log_set_upgrade_syslog_to_journal -- 强制syslog日志转到journal
│
├── [PID=1 分支]
│   ├── mount_setup_early -- 挂载早期API文件系统(/dev, /proc, /sys)
│   ├── disable_printk_ratelimit -- 禁用内核日志限流
│   ├── initialize_security -- 初始化安全子系统
│   │   ├── mac_selinux_setup -- 加载SELinux策略
│   │   ├── mac_smack_setup -- 加载SMACK策略
│   │   ├── mac_apparmor_setup -- 加载AppArmor策略
│   │   └── ima_setup -- 初始化IMA完整性架构
│   ├── initialize_clock -- 初始化系统时钟/RTC处理
│   ├── initialize_coredump -- 配置核心转储参数
│   ├── fixup_environment -- 修复PID1环境变量(如TERM)
│   ├── kmod_setup -- 加载必需的内核模块
│   ├── mount_setup -- 挂载主API文件系统
│   ├── efi_take_random_seed -- 从EFI获取随机种子
│   └── cache_efi_options_variable -- 缓存EFI启动选项
│
├── save_rlimits -- 保存原始RLIMIT_NOFILE/RLIMIT_MEMLOCK值
├── parse_configuration -- 解析配置文件和内核参数
│   ├── reset_arguments -- 重置配置参数
│   ├── parse_config_file -- 解析systemd.conf配置文件
│   ├── proc_cmdline_parse -- 解析内核命令行参数
│   │   └── parse_proc_cmdline_item -- 处理单个内核参数
│   ├── fallback_rlimit_nofile -- 设置默认文件描述符限制
│   └── fallback_rlimit_memlock -- 设置默认内存锁限制
│
├── parse_argv -- 解析命令行参数
├── safety_checks -- 执行安全策略检查
│
├── [动作分支]
│   ├── pager_open -- 打开分页器(--no-pager控制)
│   ├── help -- 显示帮助信息(-h/--help)
│   ├── version -- 显示版本信息(--version)
│   ├── unit_dump_config_items -- 导出配置项(--dump-configuration-items)
│   └── bus_manager_introspect_implementations -- D-Bus自省(--bus-introspect)
│
├── apply_clock_update -- 应用内核命令行时钟设置(systemd.clock_usec=)
├── cmdline_take_random_seed -- 应用内核命令行随机种子(systemd.random_seed=)
├── initialize_core_pattern -- 设置核心转储模式(systemd.early_core_pattern)
├── collect_fds -- 收集文件描述符用于状态反序列化
│   └── fdset_new_fill -- 填充FDSet结构
│
├── setup_console_terminal -- 设置控制台终端
│   ├── release_terminal -- 释放终端控制权
│   └── console_setup -- 重置控制台配置
│
├── log_execution_mode -- 记录启动模式(系统/用户/首次启动)
├── initialize_runtime -- 初始化运行时环境
│   ├── update_cpu_affinity -- 更新CPU亲和性(systemd.cpu_affinity)
│   ├── update_numa_policy -- 更新NUMA策略(systemd.numa_policy)
│   ├── mount_cgroup_controllers -- 挂载cgroup控制器
│   ├── status_welcome -- 显示欢迎信息(OS名称/LOGO)
│   ├── hostname_setup -- 设置主机名
│   ├── machine_id_setup -- 设置机器ID(/etc/machine-id)
│   ├── loopback_setup -- 初始化回环网络设备
│   ├── bump_unix_max_dgram_qlen -- 增加Unix域套接字队列长度
│   ├── bump_file_max_and_nr_open -- 增加系统级文件描述符限制
│   ├── test_usr -- 检查/usr独立挂载(发出兼容性警告)
│   ├── write_container_id -- 写入容器ID(/run/systemd/container)
│   ├── capability_bounding_set_drop -- 丢弃不需要的能力集
│   ├── enforce_syscall_archs -- 限制系统调用架构
│   ├── bump_rlimit_nofile -- 提升进程级文件描述符限制
│   └── bump_rlimit_memlock -- 提升进程级内存锁限制
│
├── manager_new -- 创建Manager对象(核心管理器)
├── set_manager_defaults -- 设置Manager默认参数
├── set_manager_settings -- 设置Manager运行参数
├── manager_startup -- 启动管理器
│   └── manager_deserialize -- 反序列化状态(--deserialize)
│
├── do_queue_default_job -- 排队默认任务(default.target)
│   ├── manager_load_startable_unit_or_warn -- 加载可启动单元
│   └── manager_add_job -- 添加作业到事务队列
│
├── invoke_main_loop -- 进入主事件循环
│   └── manager_loop -- 事件处理主循环
│       ├── manager_dispatch_queue -- 分发单元队列
│       └── process_unit_queue -- 处理单元状态变更
│
└── [退出分支]
    ├── become_shutdown -- 执行关机操作
    │   └── execve(systemd-shutdown) -- 移交控制权给shutdown二进制
    └── freeze_or_exit_or_reboot -- 冻结/退出/重启系统

systemd的配置文件:

配置类型路径/来源作用解析代码位置
全局配置 /etc/systemd/system.conf PID 1 运行时参数 src/core/load-fragment.c
单元文件 /usr/lib/systemd/system/*.unit
/etc/systemd/system/*.unit
服务/挂载点等定义 src/core/load-fragment.c
内核命令行 /proc/cmdline 覆盖启动目标/调试参数 src/core/kernel-command-line.c
生成器(Generators) /usr/lib/systemd/system-generators/* 动态生成单元文件(如fstab→.mount) src/core/generator.c
OS标识 /etc/os-release 操作系统信息 src/core/os-release.[c/h]
机器ID /etc/machine-id 系统唯一标识 src/core/machine-id.[c/h]
主机名 /etc/hostname 系统主机名 src/core/hostname.[c/h]

4 Target 与 Unit

 Target 本身是一种特殊的 Unit(.target)。它的主要作用是将多个其他 Unit(服务、挂载点、套接字、甚至其他Target)逻辑分组在一起。
Target作用依赖链
default.target 系统默认启动目标(通常是软链接) → graphical.target
graphical.target 带图形界面的完整系统 → multi-user.target
multi-user.target 多用户命令行系统 → basic.target
basic.target 基础系统服务就绪 → sysinit.target
sysinit.target 核心系统初始化完成 依赖所有关键初始化服务
rescue.target 单用户救援模式 仅启动基本服务
emergency.target 紧急Shell(系统完全失败时) 无服务启动

Unit 是 systemd 管理和操作的所有资源的基本抽象单位。它代表了系统启动和运行期间需要管理的各种实体。

单元类型文件后缀作用描述示例
Service .service 守护进程/一次性服务 nginx.service
Target .target 系统状态分组 multi-user.target
Socket .socket 按需激活的套接字 sshd.socket
Mount .mount 文件系统挂载点 /home.mount
Timer .timer 定时任务(替代cron) daily-backup.timer
Path .path 文件路径监控触发服务 log-watcher.path
Device .device 内核设备文件 /dev/sda1.device
Swap .swap 交换空间 swap-file.swap
Slice .slice Cgroup资源分区 user-1000.slice
Scope .scope 外部进程分组 通常由程序管理器创建

5 依赖关系形成机制

systemd 通过单元文件指令定义依赖关系,实现精确启动顺序控制:

指令作用示例
Requires= 强依赖:目标单元失败则本单元失败 Requires=network.target
Wants= 弱依赖:尝试启动目标单元,但失败不影响本单元 Wants=basic.target
After= / Before= 顺序控制:定义启动顺序(不创建依赖关系) After=syslog.target
BindsTo= 强绑定:目标单元停止时本单元也停止 BindsTo=mountpoint.mount
Conflicts= 互斥关系:本单元与目标单元不能同时运行 Conflicts=shutdown.target
PartOf= 关联停止:目标单元停止时本单元也停止(但不影响启动) PartOf=multi-user.target

 6 启动分析和优化

命令作用示例输出片段

systemd-analyze time

显示内核/用户空间启动耗时

Startup finished in 2.091s (kernel) + 28.193s (userspace) = 30.284s
graphical.target reached after 27.493s in userspace

 

systemd-analyze critical-chain

显示关键路径(最慢链)

graphical.target @27.493s
└─multi-user.target @27.472s
└─getty.target @27.450s
└─serial-getty@ttyAMA0.service @27.421s
└─dev-ttyAMA0.device @27.332s

systemd-analyze plot > boot.svg

生成启动时序图(需SVG查看器)

[图形化显示各单元启动时间线]

systemd-analyze blame

 按耗时排序单元

23.284s dev-vda.device
10.613s systemd-udev-trigger.service
7.672s networkd-dispatcher.service
6.203s systemd-resolved.service
5.624s apt-daily.service
4.440s systemd-timesyncd.service
3.916s systemd-logind.service
2.665s e2scrub_reap.service
2.255s systemd-journald.service
1.889s systemd-udevd.service
1.811s user@0.service
1.761s systemd-remount-fs.service
1.713s systemd-journal-flush.service

systemd-analyze dump > boottrace.txt

 导出详细启动数据

 

systemctl list-dependencies graphical.target

 查看图形界面的直接依赖

graphical.target
○ ├─display-manager.service
○ ├─systemd-update-utmp-runlevel.service
● └─multi-user.target
● ├─dbus.service

journalctl -b -0

查看本次启动完整日志

-- Logs begin at ...

journalctl _PID=1 --since="2 minutes ago"

过滤PID 1的日志

systemd[1]: Started Network Manager.

posted on 2025-06-07 23:59  ArnoldLu  阅读(238)  评论(0)    收藏  举报

导航