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
|
systemd-analyze critical-chain |
显示关键路径(最慢链) |
graphical.target @27.493s |
systemd-analyze plot > boot.svg |
生成启动时序图(需SVG查看器) |
[图形化显示各单元启动时间线] |
systemd-analyze blame |
按耗时排序单元 |
23.284s dev-vda.device |
systemd-analyze dump > boottrace.txt |
导出详细启动数据 |
|
systemctl list-dependencies graphical.target |
查看图形界面的直接依赖 |
graphical.target |
journalctl -b -0 |
查看本次启动完整日志 |
-- Logs begin at ... |
journalctl _PID=1 --since="2 minutes ago" |
过滤PID 1的日志 |
systemd[1]: Started Network Manager. |