systemd

最后更新: 2024-04-14 21:45:03 +0800 +0800
tags: Linux | Systemd
原文以英文撰写。您正在阅读AI翻译版本。

systemd 是服务管理器,也是 Linux 系统管理组件套件。 系统管理器以 PID 1 运行;每个用户的管理器以 systemd --user 运行。 这两类管理器都会加载单元、计算作业事务,并通过 Linux 控制组监管进程组。

单元、作业与生命周期状态 #

单元表示管理器已知的资源。常见的单元类型如下:

后缀资源
.service进程或守护进程
.socket用于激活的 IPC 或网络套接字
.target同步与分组节点
.mount、.automount、.swap存储配置
.device、.path、.timer基于设备或事件的激活
.slice、.scopecgroup 组织以及外部启动的进程

作业是请求的单元状态变更,例如 start、stop 或 reload。启动一个单元可能会引入其他作业;管理器会将它们合并为一个事务,检查排序循环和冲突作业,然后并行调度相互独立的工作。

单元状态包含多个维度。LoadState 记录定义是否已加载;ActiveState 报告 inactive、activating、active、deactivating 或 failed 等高层状态;SubState 提供类型特定的详细信息。已加载的服务不一定正在运行,已 enable 的服务也不一定已加载或处于 active 状态。enable 会修改启动或激活链接;start 会创建运行时作业。

sh
systemctl show sshd.service \
  --property=LoadState,ActiveState,SubState,UnitFileState
systemctl status sshd.service
systemctl list-jobs
systemctl --failed

要求依赖与排序依赖 #

要求边决定哪些单元会进入事务:

  • Requires= 会引入另一个单元,并传播选定的 start 失败或 stop;
  • Wants= 较弱,因为被请求的单元可能失败,但请求方不一定失败;
  • Requisite= 要求另一个单元已经处于 active 状态,否则失败;
  • BindsTo= 将活动状态更紧密地绑定到可能消失的资源;
  • Conflicts= 会安排停止冲突单元。

排序边决定执行顺序。After= 和 Before= 不会将另一个单元引入事务,而 Requires= 和 Wants= 不会建立顺序。如果两种关系都需要表达,应同时写出:

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

关闭时的排序评估方式不同:如果同一事务中一个单元被排序为晚于另一个单元停止,则前者会先停止。

管理器会根据单元类型和设置添加隐式依赖与默认依赖。例如,mount 单元会获得与父挂载点的关系,许多 service 单元会获得相对于基本启动和关闭 target 的默认排序。generator 会在管理器启动早期创建或修改单元定义;systemd-fstab-generator 会转换 /etc/fstab,其他 generator 会处理内核命令行和兼容性输入。不要假设每条边都会以字面形式出现在某个文件中,应检查计算出的图:

sh
systemctl list-dependencies example.service
systemctl list-dependencies --reverse example.service
systemctl show example.service --property=Wants,Requires,Before,After
systemd-analyze critical-chain

发现、命名与覆盖 #

单元名称通过后缀编码类型。由路径派生的名称使用 systemd 转义:/var/lib 变为 var-lib.mount,而 / 变为 -.mount。worker@.service 这样的模板名称会创建 worker@blue.service 这样的实例;%i 展开为已转义的实例,%I 展开为未转义的形式。

管理器会搜索编译时确定的单元加载路径。/etc/systemd/system 中的管理员定义优先于通常安装在 /usr/lib/systemd/system 或 /lib/systemd/system 下的供应商定义;临时目录和生成目录也在同一搜索顺序中具有规定的位置。由于不同发行版可能存在差异,应查询正在运行的管理器:

sh
systemd-analyze unit-paths
systemd-analyze --user unit-paths
systemctl cat example.service
systemctl list-unit-files 'example*'

别名是另一个有效的单元名称,可通过符号链接或 enable 时的 Alias= 实现。.wants/ 和 .requires/ 目录用于编码依赖链接。WantedBy= 和 RequiredBy= 等 [Install] 设置会告诉 systemctl enable 应创建哪些链接;管理器在启动单元时不会解释 [Install]。

不要为了本地策略编辑已打包的供应商文件。应创建 drop-in:

sh
systemctl edit example.service
systemctl daemon-reload
systemctl restart example.service
systemd-delta

名为 *.conf 的 drop-in 会按字典序合并。标量设置会替换较早的值,而列表设置可能会累积,因此替换前可能需要先进行空赋值:

ini
[Service]
ExecStart=
ExecStart=/usr/local/bin/example --foreground

systemctl revert 会删除为单元创建的本地覆盖。在重启生产服务前,使用 systemctl cat 检查合并后的定义。

服务跟踪与 cgroup #

systemd 会将服务进程放入单元 cgroup。即使经过 double-fork,后代进程仍与服务关联,因此 PID 文件不是根本性的跟踪机制。slice 会分层组织服务和 scope 的 cgroup;CPUWeight=、MemoryMax= 和 TasksMax= 等资源设置会通过主机上可用的内核 cgroup 控制器生效。

sh
systemctl status example.service
systemd-cgls
systemctl show example.service --property=ControlGroup,MainPID,TasksCurrent

Type=simple 将 ExecStart 进程视为主进程,并且不会等待 readiness 协议。Type=exec 会等待执行成功。Type=notify 会等待实现通知协议的服务发送 READY=1。Type=forking 支持传统的自行 daemon 化程序,而 Type=oneshot 会等待命令退出。应选择与守护进程匹配的 readiness 契约;将某单元排序在 simple 服务之后,只能证明其进程已启动。

target、启动与兼容性 #

default.target 是正常启动目标。内核 systemd.unit= 参数可以为某次启动选择另一个 target;否则使用已配置的默认值:

sh
systemctl get-default
systemctl set-default multi-user.target
systemctl isolate rescue.target

isolate 会停止 target 不需要的单元,并可能终止会话,因此远程使用前应检查 target 依赖关系。emergency.target 和 rescue.target 提供受限的恢复环境。

target 会对工作进行分组,但本身不会施加顺序。因此,启动性能分析和故障诊断会同时使用作业图和时间戳:

sh
systemd-analyze time
systemd-analyze blame
systemd-analyze critical-chain
journalctl -b -p warning

SysV 兼容性取决于发行版。generator 可以将传统 init 脚本包装为 service 单元,shutdown 等熟悉的命令也可能是指向 systemd 工具的链接。原生单元提供更精确的依赖关系、cgroup 跟踪、激活和沙箱;不要假设 init 脚本会获得所有原生服务语义。

logind 与用户管理器 #

systemd-logind 使用内核和用户空间的设备/会话信息跟踪 seat、登录会话、用户和 inhibitor lock。loginctl 用于检查这些状态:

sh
loginctl list-sessions
loginctl session-status
loginctl user-status "$USER"

PAM 登录通常会启动运行用户管理器的 user@UID.service。用户单元遵循独立的加载路径,并通过 systemctl --user 控制。默认情况下,管理器的生命周期遵循登录策略。loginctl enable-linger USER 会请求在启动时启动,并在最后一个会话结束后保持运行;应谨慎使用,因为 lingering 的用户服务即使没有交互式登录也会消耗资源。

系统管理器和用户管理器具有独立的权限和总线。用户服务不能仅通过声明单元选项获得系统管理器权限;除非这种耦合是有意设计的,否则系统服务也不应依赖临时桌面会话。

运行时检查 #

应使用管理器自身的接口,而不是仅根据文件推断状态:

sh
systemctl list-units --all
systemctl list-unit-files
systemctl cat example.service
systemctl show example.service
systemd-analyze verify /etc/systemd/system/example.service
journalctl --unit=example.service --boot

在 systemctl edit 之外修改单元文件后,运行 systemctl daemon-reload;该命令会重新解析定义,但不会重启服务。应先验证,再 reload,检查合并后的单元,然后重启或 reload 受影响范围最小的单元。上游手册 systemd.unit(5)、systemd.service(5)、systemd.special(7) 和 systemd.generator(7) 定义了主机上安装版本的行为。

评论

在 GitHub 上阅读或评论(发表评论需要登录)