systemd 核心功能三层模型
systemd 核心功能三层模型
一句话总结:本文以"进程怎么创建、怎么通信、创建了什么"三层模型重新梳理了 systemd 的全部源码组件,核心结论是 systemd 的本质 = PID 1 进程生命周期管理引擎 + sd-bus / D-Bus 通信基础设施 + 一系列让裸内核变得可用的守护进程。
核心认知模型
一个裸 Linux 内核启动后什么也做不了——没有网络、没有日志、设备插入无响应。systemd 做的事就三件:
┌─────────────────────────────────────────────┐
│ 第 3 层:创建了什么? │
│ 让系统变得可用的守护进程 │
│ journald / udevd / networkd / logind ... │
├─────────────────────────────────────────────┤
│ 第 2 层:怎么通信? │
│ 进程间 IPC 基础设施 │
│ sd-bus 库 + D-Bus 总线 │
├─────────────────────────────────────────────┤
│ 第 1 层:进程怎么创建? │
│ 生命周期管理引擎 │
│ PID 1 (unit / job / transaction 引擎) │
├─────────────────────────────────────────────┤
│ 支撑层:基础库 │
│ fundamental → basic → shared → libsystemd │
└─────────────────────────────────────────────┘
三层之上还有命令行工具(systemctl / journalctl / busctl),是用户与三层交互的操作界面。
总览流程图
第 1 层:进程怎么创建(PID 1 进程生命周期管理引擎)
内核已经能做 fork/exec,systemd 加了什么
内核的 fork() + exec() 只提供了机制——“创建一个进程”。systemd 的 PID 1 提供的是策略——什么时候创建、依赖谁、失败了怎么办、用多少资源、stdout 收到哪、挂了自己重启。简单说:内核造进程,systemd 管进程的一生。
核心源码:src/core/
| 核心文件 | 职责 |
|---|---|
unit.c/h |
所有可管理事物的基类:统一状态机(active / inactive / failed / reloading) |
service.c/h |
最复杂的 unit 类型:fork + exec 普通守护进程,跟踪主 PID,检测崩溃自动重启 |
socket.c/h |
socket 激活:先创建监听 socket,有请求才 wake up 服务进程 |
mount.c/h |
挂载点 unit:/etc/fstab 的动态版本 |
device.c/h |
设备 unit:与 udevd 联动,插 U 盘自动触发挂载 |
timer.c/h |
定时器 unit:替代 crond |
scope.c/h |
外部创建的进程组(如 Docker 容器、用户登录 session) |
slice.c/h |
cgroup 分层节点:/system.slice、/user.slice |
target.c/h |
逻辑分组:multi-user.target、graphical.target(替代 SysV runlevel) |
job.c/h |
job 调度引擎:按依赖图决定启动/停止的执行顺序 |
transaction.c/h |
事务引擎:验证依赖是否满足,形成可执行的 job 队列 |
manager.c/h |
全局管理者:持有所有 unit、job、cgroup 的引用,D-Bus 请求入口 |
cgroup.c/h |
cgroup 操作:创建/删除 cgroup,设置 CPU/内存限制 |
dbus-*.c/h |
PID 1 的 D-Bus 接口层(dbus-manager.c, dbus-unit.c, dbus-service.c…) |
load-fragment.c/h |
解析 unit 配置文件(*.service、*.socket、*.mount 等) |
创建流程:一个 service 从声明到运行
unit 配置文件 (/etc/systemd/system/bmcweb.service)
→ load-fragment.c 解析
→ unit.c 创建 unit 对象,状态 = inactive
→ transaction.c 检查依赖(需要 network-online.target + dbus.service)
→ job.c 生成 job 队列(先启依赖,再启本身)
→ service.c fork + exec,状态 = active
→ cgroup.c 放入 /system.slice/bmcweb.service
→ 持续监控:进程退出 → 自动重启或标记 failed
Generator:动态生成 unit 的小工具
有些 unit 不能预先写死(比如机器有几块硬盘、几个 tty),所以 PID 1 在加载 unit 之前先跑 Generator:
启动顺序:
PID 1 启动
→ 第 1 步:跑所有 Generator(一次性小程序)
fstab-generator: /etc/fstab → mount unit
getty-generator: 检测 tty → getty@ttyS0.service
sysv-generator: 兼容 /etc/init.d/ 脚本 → unit
输出:写到 /run/systemd/generator/ 临时目录
→ 第 2 步:PID 1 加载所有 unit(包括 generator 生成的)
→ 第 3 步:计算依赖图,启动服务
→ Generator 早已退出
Generator 不通过 D-Bus 通信(此时总线还没启动),直接写文件到特定目录。
第 2 层:怎么通信(sd-bus + D-Bus IPC 基础设施)
D-Bus 协议不是 systemd 发明的,sd-bus 是
D-Bus 协议和 dbus-daemon 在 2003 年就有了。systemd 做的是:
- 重写了客户端库 sd-bus(
src/libsystemd/sd-bus/,bus-message.c就是你最初打开的文件)——消息序列化/反序列化更高效 - 把 D-Bus 变成了 systemd 的控制面板——systemd 所有组件之间的通信全部走到 D-Bus 总线上
通信全景
┌─────────────────── D-Bus 总线 (dbus-daemon / dbus-broker) ───────────────┐
│ │
│ PID 1 journald udevd networkd │
│ 暴露接口: 暴露接口: 暴露接口: 暴露接口: │
│ .Manager .Journal .Device .Network │
│ .Unit (日志读写) (设备枚举) (网络配置) │
│ .Service │
│ │
│ 所有组件通过 sd-bus 库在总线上互相调用、读取属性、订阅信号 │
└──────────────────────────────────────────────────────────────────────────┘
↑ ↑ ↑ ↑
│ │ │ │
systemctl journalctl udevadm networkctl
(启停服务) (查日志) (查设备) (查网络)
命令行工具 = 用户操作界面,背后全是通过 D-Bus 与对应守护进程通信
核心源码
| 目录 | 内容 |
|---|---|
src/libsystemd/sd-bus/ |
sd-bus 核心实现:bus-message.c(序列化)、bus-control.c(连接管理)、bus-match.c(规则匹配)、bus-objects.c(对象树)、bus-signature.c(类型签名) |
src/busctl/ |
busctl 命令行工具:D-Bus 调试(类似 dbus-send / dbus-monitor) |
src/systemd/sd-bus.h |
sd-bus 公开头文件 |
通信流程示例
systemctl start bmcweb.service
→ sd_bus_call_method("StartUnit", "bmcweb.service", "replace")
→ 序列化为 D-Bus 消息 (bus-message.c)
→ 发送到 dbus-daemon
→ dbus-daemon 路由到 PID 1
→ PID 1 (core/dbus-manager.c) 反序列化 → manager_start_unit()
→ 执行完毕后 PID 1 通过 D-Bus 回复 systemctl
第 3 层:创建了什么(让系统变得可用的守护进程)
裸内核只有一个 shell。systemd 自带了一套守护进程,让系统"开箱即用":
| 守护进程 | 源码目录 | 做了什么 | 没有它的话 |
|---|---|---|---|
| journald | src/journal/ |
收集所有进程的 stdout/stderr,二进制存储,支持索引查询 | 每个服务各自写文本日志,到处找 log 文件 |
| udevd | src/udev/ |
监听内核设备事件,自动创建设备节点、加载驱动、触发挂载 | 插 U 盘完全没反应 |
| networkd | src/network/ |
网络接口配置:DHCP 获取 IP、静态 IP、VLAN、桥接 | 网卡没有 IP,手动 ifconfig |
| resolved | src/resolve/ |
DNS 缓存与解析,管理 /etc/resolv.conf |
每次 DNS 查询都直接打上游,没有缓存 |
| logind | src/login/ |
用户会话管理:多 seat、多 session、切换 tty、电源按钮处理 | 用户登录后的 session 不受管理 |
| timesyncd | src/timesync/ |
SNTP 时间同步(轻量版 ntpd) | 系统时钟慢慢漂移 |
| hostnamed | src/hostname/ |
主机名管理 | hostname 命令改了但重启丢失 |
| timedated | src/timedate/ |
时间/时区管理 | 手动 date + tzselect |
| localed | src/locale/ |
系统 locale 管理 | 手工编辑 /etc/locale.conf |
| coredumpd | src/coredump/ |
崩溃时收集 core dump,存到 journal | 进程崩了就崩了,不知道崩在哪 |
| oomd | src/oom/ |
内存压力下智能杀进程 | 内核 OOM killer 随机杀,可能杀掉关键服务 |
| nspawn | src/nspawn/ |
轻量级容器(不需要 Docker) | 没有容器环境 |
| homed | src/home/ |
可迁移加密主目录 | 传统 /etc/passwd + /home |
加上 units/ 目录下的预置配置(basic.target、multi-user.target、getty@.service 等几百个文件),systemd 把一个裸内核变成了"通电就能用"的完整系统。
支撑层:基础库的分层架构
以上三层都依赖底层库,库的分层规则是每层只能向下依赖,不能向上:
src/fundamental/ 最基础的类型和宏,连 basic 都依赖它
↑
src/basic/ 底层工具(alloc-util, hashmap, string-util, fileio, cgroup-util...)
↑
src/shared/ 系统级共享模块(比 basic 更高层,可调 mount/umount 等系统调用)
↑
src/libsystemd/ 公开 API(sd-bus / sd-event / sd-journal / sd-daemon / sd-login)
↑
┌──────────────────┬──────────────────┬────────────────────┐
src/core/ src/journal/ src/systemctl/
(PID 1) (journald) (命令行工具)
关键规则:
basic/里的代码不能 includeshared/或core/的内部头文件(底层不知道上层存在)core/可以 include 下面所有层- 外部程序(如 bmcweb)只应链接
libsystemd,不能 includecore/的内部头文件
汇总:一条命令穿过所有三层
你敲下: systemctl start bmcweb.service
│
│ [第 2 层:通信]
│ 1. systemctl 通过 sd-bus 将 "StartUnit" 序列化为 D-Bus 消息
│ 2. dbus-daemon 路由消息到 PID 1
│
│ [第 1 层:进程创建]
│ 3. PID 1 (dbus-manager.c) 反序列化 → manager_start_unit()
│ 4. transaction.c 检查依赖:network-online.target + dbus.service
│ 5. job.c 按依赖顺序排队:先启依赖,再启 bmcweb
│ 6. service.c fork() + exec("/usr/bin/bmcweb")
│ 7. cgroup.c 放入 /system.slice/bmcweb.service,设置资源限制
│
│ [第 3 层:配套设施]
│ 8. journald 自动收集 bmcweb 的 stdout/stderr 到日志
│ 9. networkd 在此之前已配好网络(bmcweb 需要监听端口)
│
▼ bmcweb 运行中,systemd 持续监控:崩了自动重启,D-Bus 接口可查询状态
学习路径
| 顺序 | 目标 | 读什么 |
|---|---|---|
| 1 | 理解进程怎么创建 | src/core/unit.h → unit.c → job.c → transaction.c → service.c |
| 2 | 理解怎么通信 | src/libsystemd/sd-bus/bus-message.c(序列化)→ bus-control.c(连接)→ bus-match.c(路由) |
| 3 | 理解创建了什么 | units/*.service → src/journal/ → src/network/(挑一个感兴趣的 daemon) |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)