systemd 教程 / systemd 简介与架构
systemd 简介与架构
一、历史背景
1.1 从 System V init 到 systemd
Linux 系统启动过程经历了三代初始化系统:
| 代际 | 系统 | 引入时间 | 特点 |
|---|---|---|---|
| 第一代 | System V init | 1983 (Unix) | 串行启动,Shell 脚本驱动 |
| 第二代 | Upstart | 2006 (Ubuntu) | 事件驱动,并行启动 |
| 第三代 | systemd | 2010 (Fedora) | 并行启动,依赖管理,全面接管 |
systemd 由 Lennart Poettering(红帽工程师)于 2010 年首次发布,最初目标是替代传统的 System V init 系统。如今 systemd 已经远远超出"初始化系统"的范畴,成为 Linux 系统的基础服务管理框架。
# 查看当前系统的 systemd 版本
systemctl --version
输出示例:
systemd 252 (252.36-1~deb12u1)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified
1.2 systemd 的设计哲学
systemd 遵循以下设计原则:
- 并行化启动:尽可能同时启动无依赖关系的服务
- 按需激活:通过 socket/D-Bus 激活,减少不必要的启动
- 依赖管理:声明式依赖关系,自动解析
- 统一日志:journald 提供结构化二进制日志
- CGroup 管理:每个服务独立的资源隔离
二、三代初始化系统详细对比
| 特性 | System V init | Upstart | systemd |
|---|---|---|---|
| 启动方式 | 串行 | 事件驱动 | 并行 + 依赖 |
| 服务定义 | Shell 脚本 | 事件配置 | Unit 文件 |
| 依赖管理 | 手动排序 | 事件触发 | 声明式 |
| 日志系统 | syslog | syslog | journald |
| CGroup 集成 | 无 | 有限 | 深度集成 |
| 服务监控 | 无 | 有限 | 自动重启 |
| 进程追踪 | PID 文件 | D-Bus | CGroup + PID |
| 启动速度 | 慢 | 较快 | 快 |
| 配置语法 | Shell | 自有语法 | INI 风格 |
2.1 System V init 的局限
# 传统 SysVinit 启动脚本示例
cat /etc/init.d/networking
System V init 依赖 /etc/inittab 定义运行级别,服务脚本放在 /etc/init.d/ 下,通过符号链接到 /etc/rcN.d/ 目录控制启动顺序。缺点包括:
- 串行启动,速度慢
- Shell 脚本复杂、难以维护
- 无法自动重启崩溃的服务
- 缺乏统一的服务管理接口
三、systemd 架构总览
3.1 核心组件
systemd 不仅仅是一个 init 系统,而是一组相互协作的组件:
┌─────────────────────────┐
│ systemd (PID 1) │
│ 系统与服务管理器核心 │
└───────────┬─────────────┘
│
┌───────────┬───────────┼───────────┬───────────┐
│ │ │ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│journald │ │networkd │ │resolved │ │logind │ │timesyncd│
│日志系统 │ │网络管理 │ │DNS解析 │ │登录管理 │ │时间同步 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
| 组件 | 守护进程 | 功能 |
|---|---|---|
| systemd | PID 1 | 核心进程管理器 |
| journald | systemd-journald | 结构化日志收集与查询 |
| networkd | systemd-networkd | 网络配置管理 |
| resolved | systemd-resolved | DNS 解析与缓存 |
| logind | systemd-logind | 用户登录与会话管理 |
| timesyncd | systemd-timesyncd | NTP 时间同步 |
| udevd | systemd-udevd | 设备管理 |
| home | systemd-homed | 用户主目录管理 |
3.2 PID 1 的特殊角色
systemd 作为 PID 1 运行,承担以下特殊职责:
# 确认 systemd 是 PID 1
ps -p 1 -o comm=
输出:
systemd
PID 1 的特殊性:
- 孤儿进程回收:所有孤儿进程会被 reattach 到 PID 1
- 信号处理:PID 1 不响应
SIGTERM(防止意外杀掉 init) - CGroup 树根:所有进程都位于 systemd 管理的 CGroup 树中
⚠️ 注意:不要尝试 kill PID 1,这会导致系统立即重启(systemctl reboot 是正确方式)。
四、Unit 类型
systemd 管理的基本对象称为 Unit,每种 Unit 类型对应不同的系统资源:
| Unit 类型 | 文件后缀 | 用途 |
|---|---|---|
| Service | .service | 系统服务(最常用) |
| Socket | .socket | Socket 激活 |
| Timer | .timer | 定时任务(替代 cron) |
| Mount | .mount | 文件系统挂载 |
| Automount | .automount | 按需自动挂载 |
| Swap | .swap | 交换分区管理 |
| Path | .path | 路径监控触发 |
| Target | .target | 启动目标(分组单元) |
| Device | .device | 设备单元(udev) |
| Slice | .slice | CGroup 资源切片 |
| Scope | .scope | 外部创建的 CGroup |
| Slice | .slice | CGroup 层级组织 |
# 列出所有 Unit 类型
systemctl list-unit-files --type=help
五、Unit 文件路径与优先级
5.1 三个搜索路径
systemd 按以下路径搜索 Unit 文件:
| 路径 | 用途 | 优先级 |
|---|---|---|
/etc/systemd/system/ | 系统管理员自定义(最高优先级) | ★★★ |
/run/systemd/system/ | 运行时生成(重启后丢失) | ★★ |
/usr/lib/systemd/system/ | 软件包安装的默认文件 | ★ |
💡 提示:/usr/local/lib/systemd/system/ 在某些发行版中也支持,用于手动安装的软件。
5.2 用户级 Unit 路径
| 路径 | 用途 |
|---|---|
~/.config/systemd/user/ | 用户自定义(最高优先级) |
/run/user/<UID>/systemd/user/ | 运行时用户单元 |
/usr/lib/systemd/user/ | 软件包安装的用户单元 |
5.3 优先级规则
当同名 Unit 文件存在于多个路径时,systemd 按优先级加载:
# 查看某 Unit 的实际加载路径
systemctl cat sshd.service
输出会显示完整路径,高优先级目录中的文件会完全覆盖低优先级目录中的同名文件(非合并)。
# 查看 Unit 文件的搜索路径
systemd-analyze unit-paths
输出示例:
/etc/systemd/system.control
/run/systemd/system.control
/run/systemd/transient
/run/systemd/generator.early
/etc/systemd/system
/etc/systemd/system.attached
/run/systemd/system
/run/systemd/system.attached
/run/systemd/generator
/usr/local/lib/systemd/system
/usr/lib/systemd/system
/run/systemd/generator.late
⚠️ 注意:直接修改 /usr/lib/systemd/system/ 下的文件会在软件包更新时被覆盖。正确做法是将自定义配置放在 /etc/systemd/system/ 中。
六、发行版支持情况
| 发行版 | 默认使用 systemd | 备注 |
|---|---|---|
| Fedora | ✅ (2011, v15) | systemd 的首发平台 |
| Ubuntu | ✅ (2014, v14.10) | 从 Upstart 迁移 |
| Debian | ✅ (2015, v8) | 经过社区投票决定 |
| CentOS/RHEL | ✅ (v7+) | RHEL 7 开始 |
| Arch Linux | ✅ (2012) | 较早采纳 |
| openSUSE | ✅ (v12.1+) | — |
| Alpine Linux | ❌ | 使用 OpenRC |
| Void Linux | ❌ | 使用 runit |
| Gentoo | 可选 | 默认 OpenRC |
# 检查当前系统是否使用 systemd
ps -p 1 -o comm= | grep -q systemd && echo "使用 systemd" || echo "未使用 systemd"
七、快速上手:第一个 systemctl 命令
# 查看 sshd 服务状态
systemctl status sshd
# 启动/停止/重启服务
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
# 开机自启
systemctl enable nginx
八、生产场景
场景:快速排查系统启动问题
# 查看本次启动耗时
systemd-analyze
# 查看启动链中最慢的服务
systemd-analyze blame | head -20
# 查看关键路径(critical chain)
systemd-analyze critical-chain
输出示例:
The time after the unit is active or started is printed after the "@" character.
The time the unit takes to start is printed after the "+" character.
multi-user.target +3.102s
└─nginx.service @1.891s +210ms
└─network.target @1.880s
└─NetworkManager.service @1.203s +676ms
└─basic.target @1.198s
└─sockets.target @1.197s
└─dbus.socket @1.196s
└─sysinit.target @1.190s
└─systemd-update-utmp.service @1.175s +14ms