首页 / 内容指南 / 当前文章

systemd 的 Wants、Requires、Requisite、BindsTo、PartOf 和 After 有什么区别?

发布于 2026-08-26 · Content Fleet 编辑部

systemd 的 Wants、Requires、Requisite、BindsTo、PartOf 和 After 有什么区别?

六个指令快速对比

表格是简化模型。实际结果还受 unit 类型、默认依赖、条件、作业事务和服务是否保持 active 等因素影响。诊断时应读取 systemd 合并后的属性,而不是只看一个文件中的两行配置。

Wants:弱需求,依赖失败不阻断主体

Wants= 会在当前 unit 被激活时,把列出的 unit 一并加入启动事务。但是对方不能加入事务或启动失败时,当前 unit 仍可以继续启动。systemd 上游文档称它为弱需求依赖,并建议许多配套关系优先使用 Wants,以提升系统面对局部故障时的健壮性。

[Unit]
Wants=metrics-agent.service
After=metrics-agent.service

这里应用会尝试拉起指标代理,并排在其启动作业之后;即使指标代理失败,应用仍可启动。若省略 After=,两个启动作业可能并行执行。是否真的需要排序要根据应用行为判断:仅仅“希望它也运行”并不总意味着必须等待它。

WantedBy= 不是同一个位置的反向写法。它用于 [Install] 段,systemctl enable 会据此创建 .wants/ 符号链接,使指定 target 对当前 unit 获得 Wants 关系。WantedBy= 不会在每次手工启动当前服务时反向启动那个 target。

Requires:更强的需求,但仍不自带顺序

Requires= 会拉入目标 unit,并建立比 Wants 更强的失败和停止传播关系。若当前 unit 同时设置 After=,而所需 unit 的启动失败,当前 unit 不会启动。目标 unit 被显式停止或重启时,相应动作也会传播到依赖它的 unit。

[Unit]
Requires=database.service
After=database.service

这段配置表达“启动应用时也启动数据库,并等数据库的启动作业完成;数据库启动失败则不启动应用”。但 After= 等待的是 systemd 认定的启动完成点,这又取决于数据库服务的 Type= 和就绪通知。若数据库使用 Type=simple,systemd 可能在进程刚创建时就放行应用,并不保证端口已监听或迁移已完成。

Requires= 也不等于“只要目标后来任何方式变成 inactive,当前 unit 都必须停止”。上游文档明确说明,目标因条件检查未满足而跳过、服务自行正常退出或设备被拔出等情形,不一定按 Requires 传播。需要严格共存时应评估 BindsTo=After=

Requisite:要求已经活动,但不会帮你启动

Requisite= 与 Requires 类似,但它不会把目标 unit 启动起来。若目标尚未 active,当前 unit 的启动会立即失败。这适合“当前操作只允许在某个既有会话、挂载或基础服务已存在时执行”的场景。

[Unit]
Requisite=maintenance-window.target
After=maintenance-window.target

如果维护窗口 target 没有预先启动,这个 unit 直接失败,而不是擅自开启维护窗口。Requisite= 同样不隐含顺序;当两个 unit 已在同一事务中时,仍应使用 After= 保证检查和启动的合理次序。

不要用 Requisite 代替运行时健康检查。它判断的是 systemd 的 unit 活动状态,不会自动验证数据库能否执行查询、磁盘空间是否充足或远端 API 是否可用。业务可用性仍需服务自己的重试、超时与健康机制。

BindsTo:要求生命周期严格绑定

BindsTo= 比 Requires 更强。除了目标被显式停止或重启的传播,它还会在目标意外变为 inactive 时停止当前 unit,例如服务主进程自行终止、设备被拔出或挂载在 systemd 之外被卸载。

[Unit]
BindsTo=data.mount
After=data.mount

与同一目标的 After= 配合后,当前 unit 要保持 active,目标也必须处于 active。若挂载因条件检查未通过而被跳过,已经运行的绑定服务也会被停止。这适合数据卷、硬件设备等不可缺失资源,但对普通网络服务可能过强:短暂重启一个配套组件就会扩大中断范围。

使用 BindsTo 前应明确恢复策略。它能保证依赖对象消失时停止当前 unit,却不必然在目标恢复后自动把当前 unit 再次启动。是否重新拉起取决于其他依赖、触发关系和重启策略,不能只靠“绑定”二字推断完整自愈行为。

PartOf:只传播停止与重启

PartOf= 让当前 unit 成为另一个 unit 生命周期的一部分。当所列 unit 被 systemd 停止或重启时,动作传播到当前 unit;但启动当前 unit 不会拉起上层 unit,启动上层 unit 也不会仅凭 PartOf 自动拉起当前 unit。

[Unit]
PartOf=web-stack.target

[Install]
WantedBy=web-stack.target

这里 WantedBy= 经 enable 创建的 Wants 关系负责“启动 web-stack 时拉起服务”,PartOf= 负责“停止或重启 web-stack 时让服务跟随”。两者组合才能表达常见的组件归属。PartOf 是单向的:单独重启组件不会反向重启整个 stack。

需要注意传播的是 systemd 的停止和重启作业,而不是所有进程层面的退出。若服务自身崩溃,PartOf 不会因此停止它所隶属的 target。服务崩溃后的处理应由 Restart=、监控或更合适的依赖关系负责。

After 与 Before:只排序,不负责拉起

After=b.service 表示当两个 unit 都在同一事务中启动时,当前 unit 的启动作业排在 b 之后;停止时顺序相反。Before= 表达相反方向。它们不会让一个本来不在事务中的 unit 自动加入,也不会仅因对方失败就决定当前 unit 失败。

[Unit]
Wants=network-online.target
After=network-online.target

上例的 Wants 负责拉入,After 负责排序。如果只有 After=network-online.target,而没有其他依赖或系统机制拉入该 target,After 不会凭空启动它。这一规则同样适用于数据库、挂载和自定义 target。

“After”也不是脚本式的等待任意业务条件。systemd 只等待目标 unit 的启动作业完成。若应用必须等到外部数据库接受查询,应该使用正确的服务就绪协议、有限重试或专门的准备 unit,而不是简单增加休眠时间。

启动失败和运行后退出要分开看

依赖语义经常因时间点不同而变化。目标在启动事务中失败,与它成功启动后数分钟自行退出不是同一事件。Wants 对两者都较宽松;Requires 配合 After 能处理启动失败,但对所有意外 inactive 情形并不严格;BindsTo 才扩展到目标意外失活。

Type=oneshot 服务执行成功后如果没有 RemainAfterExit=yes,可能回到 inactive。把另一个服务 BindsTo 这种 oneshot,可能导致依赖方无法维持 active。设计依赖前要先理解目标 unit 的活动状态生命周期,不能只根据程序是否“执行成功”下结论。

条件指令也有特殊语义。目标因 ConditionPathExists= 等条件不满足而跳过,并不总被视为普通启动失败;Requires 未必阻止依赖方,而 BindsTo 配合 After 会形成更严格的共存约束。应使用测试环境实际验证目标发行版上的 systemd 行为。

如何检查合并后的真实依赖

先用 systemctl cat 查看原始 unit 与所有 drop-in,避免只读 /usr/lib/systemd/system 中的厂商文件而漏掉 /etc/systemd/system/*.d/ 覆盖。再用 systemctl show 读取 Wants、Requires、Requisite、BindsTo、PartOf、After 和 Before 等合并属性。

systemctl cat app.service
systemctl show app.service -p Wants -p Requires -p Requisite -p BindsTo -p PartOf -p After -p Before
systemctl list-dependencies app.service

复杂系统可用 systemd-analyze dot 生成依赖图,并分别筛选 requirement 与 ordering 关系。图中边很多时,应围绕目标 unit 缩小范围,结合日志中的 job 事务和时间戳判断,避免把默认依赖误认为手工配置。

修改 unit 后运行 systemd-analyze verify 检查文件,再执行 systemctl daemon-reload 让管理器重新加载。生产环境是否重启服务属于单独变更,应先评估影响;仅 daemon-reload 不会自动把所有运行中服务按新配置重启。

常见问题

写了 Requires 为什么两个服务仍同时启动?

因为 Requires 定义需求关系,不定义顺序。若必须先完成依赖 unit 的启动作业,应同时配置 After=。还要确保目标服务的 Type 能准确表达就绪时间。

只有 After 会自动启动目标服务吗?

不会。After 只在两个 unit 都进入事务时排序。需要拉入目标时,再按容错需求选择 Wants、Requires 或其他触发关系。

Requires 和 BindsTo 应该怎么选?

普通强依赖通常先评估 Requires;若目标设备、挂载或服务意外变为 inactive 时,当前 unit 也绝不能继续 active,再考虑 BindsTo,并通常配合 After。

PartOf 能让上层 target 启动子服务吗?

不能。PartOf 主要传播上层的停止和重启。要在 target 启动时拉入子服务,可通过 Wants/Requires 或 [Install] 中的 WantedBy=/RequiredBy= 建立关系。

enable 和 start 为什么表现不同?

enable 根据 [Install] 创建依赖链接,决定未来某个 target 被激活时是否拉入服务;start 是立即提交启动作业。启用不会必然立即启动,启动也不会必然创建开机依赖。

参考资料

1. systemd upstream manual, systemd.unit: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html

2. systemd upstream manual, systemd.service: https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html

3. systemd upstream manual, systemctl: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html

4. systemd upstream manual, systemd-analyze: https://www.freedesktop.org/software/systemd/man/latest/systemd-analyze.html

5. systemd upstream manual, systemd.target: https://www.freedesktop.org/software/systemd/man/latest/systemd.target.html