systemd

来自 ArchWiki
(重定向自 守护进程

来自 项目网页

systemd 是一套 Linux 系统的基本构建模块。它提供了一个系统和服务管理器,以 PID 1 运行并启动系统的其余部分。systemd 提供了强大的并行化能力,使用套接字和 D-Bus 激活来启动服务,提供按需启动守护程序,使用 Linux 控制组 跟踪进程,维护挂载点和自动挂载点,并实现精细的基于事务的依赖服务控制逻辑。systemd 支持 SysV 和 LSB init 脚本,并可作为 sysvinit 的替代品。其他部分包括日志守护程序、用于控制基本系统配置(如主机名、日期、区域设置)的实用程序、维护已登录用户和正在运行的容器和虚拟机列表、系统帐户、运行时目录和设置,以及用于管理简单网络配置、网络时间同步、日志转发和名称解析的守护程序。

从历史上看,systemd 所谓的“服务”曾被称为守护进程:任何作为“后台”进程(没有终端或用户界面)运行的程序,通常等待事件发生并提供服务。一个很好的例子是等待请求以交付页面的 Web 服务器,或者等待有人尝试登录的 SSH 服务器。虽然这些是功能齐全的应用程序,但也存在一些工作不太明显的守护进程。守护进程用于将消息写入日志文件(例如 syslogmetalog)或保持系统时间准确(例如 ntpd)等任务。有关更多信息,请参阅 daemon(7)

注意: 有关 Arch 迁移到 systemd 的详细解释,请参阅 此论坛帖子

Basic systemctl usage

用于检查和控制 systemd 的主要命令是 systemctl。它的一些用途是检查系统状态以及管理系统和服务。有关更多详细信息,请参阅 systemctl(1)

提示
  • 你可以将以下所有 systemctl 命令与 -H user@host 开关一起使用,以控制远程计算机上的 systemd 实例。这将使用 SSH 连接到远程 systemd 实例。
  • Plasma 用户可以安装 systemdgenie 作为 systemctl 的图形前端。安装后,该模块将添加到系统下。

使用单元

单元通常包括但不限于服务 (.service)、挂载点 (.mount)、设备 (.device) 和套接字 (.socket)。

使用 systemctl 时,通常必须指定单元文件的完整名称,包括其后缀,例如 sshd.socket。但是,在以下 systemctl 命令中指定单元时,有一些简短形式

  • 如果未指定后缀,systemctl 将假定为 .service。例如,netctlnetctl.service 是等效的。
  • 挂载点将自动转换为相应的 .mount 单元。例如,指定 /home 等同于 home.mount
  • 与挂载点类似,设备会自动转换为相应的 .device 单元,因此指定 /dev/sda2 等同于 dev-sda2.device

有关详细信息,请参阅 systemd.unit(5)

注意: 某些单元名称包含 @ 符号(例如 name@string.service):这意味着它们是模板单元的实例,其实际文件名不包含 string 部分(例如 name@.service)。string 称为实例标识符,类似于传递给模板单元的参数,当使用 systemctl 命令调用时:在单元文件中,它将替换 %i 说明符。更准确地说,尝试实例化 name@.suffix 模板单元之前systemd 实际上会查找具有完全 name@string.suffix 文件名的单元,尽管按照惯例,这种“冲突”很少发生,即大多数包含 @ 符号的单元文件都旨在作为模板。此外,如果调用模板单元时没有实例标识符,通常会失败(某些 systemctl 命令(如 cat)除外)。

下表中的命令对系统单元进行操作,因为 --systemsystemctl 的隐含默认设置。要改为对用户单元(对于调用用户)进行操作,请使用 systemctl --user,无需 root 权限。另请参阅 systemd/User#基本设置 以启用/禁用所有用户的用户单元。

提示
  • 大多数命令在指定多个单元时也有效,有关更多信息,请参阅 systemctl(1)
  • --now 开关可以与 enabledisablemask 结合使用,以分别立即启动、停止或屏蔽单元,而不是在重启后。
  • 一个软件包可能会为不同的目的提供单元。如果您刚刚安装了一个软件包,则可以使用 pacman -Qql package | grep -Fe .service -e .socket 来检查并找到它们。
  • 如果可用,启用 unit.socket 而不是 unit.service 可能会更好,因为套接字会在必要时启动服务。有关更多详细信息,请参阅 #Socket 激活
操作 命令 注意
分析系统状态
显示系统状态 systemctl status
列出正在运行的单元 systemctl
systemctl list-units
列出失败的单元 systemctl --failed
列出已安装的单元文件1 systemctl list-unit-files
显示 PID 的进程状态 systemctl status pid cgroup 切片、内存和父进程
检查单元状态
显示与单元关联的手册页 systemctl help unit 由单元支持
单元的状态 systemctl status unit 包括它是否正在运行
检查单元是否已启用 systemctl is-enabled unit
启动、重启、重新加载单元
立即启动单元 systemctl start unit 以 root 身份
立即停止单元 systemctl stop unit 以 root 身份
重启单元 systemctl restart unit 以 root 身份
重新加载单元及其配置 systemctl reload unit 以 root 身份
重新加载 systemd 管理器配置2 systemctl daemon-reload 以 root 身份 扫描新的或更改的单元
启用单元
启用单元以在启动时自动启动 systemctl enable unit 以 root 身份
启用单元以在启动时自动启动并立即启动 systemctl enable --now unit 以 root 身份
禁用单元使其不再在启动时启动 systemctl disable unit 以 root 身份
重新启用单元3 systemctl reenable unit 以 root 身份 即,禁用并重新启用
屏蔽单元
屏蔽单元使其无法启动4 systemctl mask unit 以 root 身份
取消屏蔽单元 systemctl unmask unit 以 root 身份
  1. 有关可用单元文件所在目录,请参阅 systemd.unit(5) § UNIT FILE LOAD PATH
  2. 这不会要求更改的单元重新加载其自己的配置(请参阅重新加载操作)。
  3. 例如,如果自上次启用以来,其 [Install] 部分已更改。
  4. 手动和作为依赖项,这使得屏蔽很危险。使用以下命令检查现有的已屏蔽单元
    $ systemctl list-unit-files --state=masked

电源管理

作为非特权用户,polkit 对于电源管理是必要的。如果您处于本地 systemd-logind 用户会话中且没有其他会话处于活动状态,则以下命令将在没有 root 权限的情况下工作。如果不是(例如,因为另一个用户已登录到 tty),systemd 将自动要求您输入 root 密码。

操作 命令
关闭并重启系统 systemctl reboot
关闭并关闭系统电源 systemctl poweroff
挂起系统 systemctl suspend
使系统进入休眠状态(将 RAM 写入磁盘) systemctl hibernate
使系统进入混合睡眠状态(也称为 suspend-to-both,它将 RAM 保存到磁盘,然后挂起) systemctl hybrid-sleep
首先挂起系统,然后在配置的时间后唤醒,以便仅使系统休眠 systemctl suspend-then-hibernate
使用 #软重启 执行仅限用户空间的重启。 systemctl soft-reboot

软重启

软重启是一种特殊类型的仅限用户空间的重启操作,不涉及内核。它由 systemd-soft-reboot.service(8) 实现,可以通过 systemctl soft-reboot 调用。与 kexec 一样,它跳过固件重新初始化,但此外,系统不会经历内核初始化和 initramfs,并且解锁的 dm-crypt 设备保持连接。

/run/nextroot/ 包含有效的根文件系统层次结构(例如,是另一个发行版或另一个快照的根挂载)时,soft-reboot 会将系统根切换到其中,从而允许切换到另一个安装,而不会丢失内核管理的状态,例如 网络

提示: /run/nextroot/ 不一定是挂载点或由物理设备支持。例如,它可以驻留在 /run/ tmpfs 中。systemd 会在 soft-reboot 上自动将 /run/nextroot/ 转换为挂载点。
注意: 在涉及内核和 initramfs 的软件包更新后,请勿调用 systemctl soft-reboot

编写单元文件

systemd 单元文件 (systemd.unit(5)) 的语法灵感来自 XDG Desktop Entry Specification .desktop 文件,而后者又受到 Microsoft Windows .ini 文件 的启发。单元文件从多个位置加载(要查看完整列表,请运行 systemctl show --property=UnitPath),但主要的加载位置是(按优先级从低到高列出)

  • /usr/lib/systemd/system/:已安装软件包提供的单元
  • /etc/systemd/system/:系统管理员安装的单元
注意
  • 用户模式 下运行 systemd 时,加载路径完全不同。
  • systemd 单元名称只能包含 ASCII 字母数字字符、下划线和句点。所有其他字符必须替换为 C 风格的“\x2d”转义符,或使用其预定义的语义('@'、'-')。有关更多信息,请参阅 systemd.unit(5)systemd-escape(1)

查看软件包安装的单元作为示例,以及 systemd.service(5) § EXAMPLES

提示:# 开头的注释也可以在单元文件中使用,但只能在新行中使用。请勿在 systemd 参数后使用行尾注释,否则单元将无法激活。

systemd-analyze(1) 可以帮助验证工作。请参阅该页面的 systemd-analyze verify FILE... 部分。

处理依赖关系

使用 systemd,可以通过正确设计单元文件来解决依赖关系。最典型的情况是,单元 A 需要单元 BA 启动之前运行。在这种情况下,将 Requires=BAfter=B 添加到 A[Unit] 部分。如果依赖关系是可选的,则添加 Wants=BAfter=B。请注意,Wants=Requires= 并不意味着 After=,这意味着如果未指定 After=,则两个单元将并行启动。

依赖关系通常放在服务上,而不是 #目标 上。例如,network.target 由配置网络接口的任何服务拉入,因此将自定义单元排序在其后就足够了,因为 network.target 无论如何都会启动。

服务类型

编写自定义服务文件时,需要考虑几种不同的启动类型。这通过 [Service] 部分中的 Type= 参数设置

  • Type=simple(默认):systemd 认为服务已立即启动。进程不得 fork。如果其他服务需要在此服务上排序,请勿使用此类型,除非它是套接字激活的。
  • Type=forkingsystemd 认为服务在进程 fork 并且父进程已退出后启动。对于经典守护进程,除非您知道没有必要,否则请使用此类型。您还应该指定 PIDFile=,以便 systemd 可以跟踪主进程。
  • Type=oneshot:这对于执行单项作业然后退出的脚本很有用。您可能还需要设置 RemainAfterExit=yes,以便 systemd 在进程退出后仍将服务视为活动状态。设置 RemainAfterExit=yes 适用于更改系统状态的单元(例如,挂载某些分区)。另请参阅 [1],了解 simple 和 oneshot 的区别。
  • Type=notify:与 Type=simple 相同,但规定守护进程在准备就绪时会向 systemd 发送信号。此通知的参考实现由 libsystemd-daemon.so 提供。
  • Type=dbus:当指定的 BusName 出现在 DBus 的系统总线上时,服务被视为已准备就绪。
  • Type=idlesystemd 将延迟服务二进制文件的执行,直到所有作业都已调度。除此之外,行为与 Type=simple 非常相似。

有关 Type 值的更详细说明,请参阅 systemd.service(5) § OPTIONS 手册页。

编辑提供的单元

为避免与 pacman 冲突,不应直接编辑软件包提供的单元文件。有两种安全的方法可以在不触及原始文件的情况下修改单元:创建一个新的单元文件,该文件覆盖原始单元,或者创建 drop-in 代码段,这些代码段应用于原始单元之上。对于这两种方法,您都必须在之后重新加载单元以应用更改。这可以通过使用 systemctl edit 编辑单元(这将自动重新加载单元)或通过重新加载所有单元来完成

# systemctl daemon-reload
提示
  • 您可以使用 systemd-delta 查看哪些单元文件已被覆盖或扩展,以及具体更改了哪些内容。
  • 使用 systemctl cat unit 查看单元文件的内容以及所有关联的 drop-in 代码段。

替换单元文件

要替换单元文件 /usr/lib/systemd/system/unit,请创建文件 /etc/systemd/system/unit重新启用单元以更新符号链接。

或者,运行

# systemctl edit --full unit

这将在您的编辑器中打开 /etc/systemd/system/unit(如果尚不存在,则复制已安装的版本),并在您完成编辑后自动重新加载它。

注意: 即使 Pacman 将来更新原始单元,替换单元也将继续使用。此方法使系统维护更加困难,因此首选下一种方法。

Drop-in 文件

要为单元文件 /usr/lib/systemd/system/unit 创建 drop-in 文件,请创建目录 /etc/systemd/system/unit.d/ 并在其中放置 .conf 文件以覆盖或添加新选项。systemd 将解析这些文件并将其应用于原始单元之上。

最简单的方法是运行

# systemctl edit unit --drop-in=drop_in_name

这将在您的文本编辑器中打开文件 /etc/systemd/system/unit.d/drop_in_name.conf(如果需要,则创建它),并在您完成编辑后自动重新加载单元。省略 --drop-in= 选项将导致 systemd 使用默认文件名 override.conf

注意
  • 密钥必须仍然放置在覆盖文件中的相应部分中。
  • 并非所有密钥都可以使用 drop-in 文件覆盖。例如,要更改 Conflicts=,需要替换文件
  • 您可以使用顶级 drop-in 文件来影响所有相同类型的单元。例如,/etc/systemd/system/service.d/ 中的 drop-in 文件会影响所有 .service 单元。您可以在 #通知有关失败的服务 中看到一个示例

恢复到供应商版本

要恢复使用 systemctl edit 对单元所做的任何更改,请执行

# systemctl revert unit

示例

例如,如果您只想向单元添加额外的依赖项,则可以创建以下文件

/etc/systemd/system/unit.d/customdependency.conf
[Unit]
Requires=new dependency
After=new dependency

再举一个例子,为了替换 ExecStart 指令,请创建以下文件

/etc/systemd/system/unit.d/customexec.conf
[Service]
ExecStart=
ExecStart=new command

请注意,ExecStart 必须在重新分配之前清除 [2]。对于可以多次指定的每个项目(例如,计时器的 OnCalendar),情况也是如此。

再举一个自动重启服务的例子

/etc/systemd/system/unit.d/restart.conf
[Service]
Restart=always
RestartSec=30

服务日志级别

对于直接将日志发送到 journald 或 syslog 的服务,您可以使用上述方法,通过在 [Service] 部分中为 LogLevelMax= 参数设置介于 0 和 6 之间的数值来控制其详细程度。例如

/etc/systemd/system/unit.d/override.conf
[Service]
LogLevelMax=3

标准日志级别与用于过滤 journal 的日志级别相同。设置较低的数字会从您的日志中排除所有更高和不太重要的日志消息。

抑制服务的标准输出

如果服务正在回显 stdout 和/或 stderr 输出,则默认情况下,这也将最终出现在日志中。可以通过在 [Service] 部分中设置 StandardOutput=null 和/或 StandardError=null 来抑制此行为。可以使用除 null 以外的其他值来进一步调整此行为。请参阅 systemd.exec(5) § LOGGING_AND_STANDARD_INPUT/OUTPUT

目标

systemd 使用目标通过依赖关系将单元组合在一起,并作为标准化的同步点。它们的作用类似于 运行级别,但行为略有不同。每个目标都已命名而不是编号,并且旨在服务于特定目的,并且有可能同时激活多个目标。某些目标通过继承另一个目标的所有服务并向其添加其他服务来实现。有一些 systemd 目标 模仿了常见的 SystemVinit 运行级别。

获取当前目标

以下内容应在 systemd 下使用,而不是运行 runlevel

$ systemctl list-units --type=target

创建自定义目标

在 sysvinit 下具有定义含义的运行级别(即 0、1、3、5 和 6);与特定的 systemd 目标 具有 1:1 映射。不幸的是,对于用户定义的运行级别(如 2 和 4),没有好的方法可以做到这一点。如果您使用这些运行级别,建议您创建一个新的命名 systemd 目标,如 /etc/systemd/system/your target,它以现有的运行级别之一作为基础(您可以查看 /usr/lib/systemd/system/graphical.target 作为示例),创建一个目录 /etc/systemd/system/your target.wants,然后从 /usr/lib/systemd/system/ 链接您希望启用的其他服务。

SysV 运行级别和 systemd 目标之间的映射

SysV 运行级别 systemd 目标 注释
0 poweroff.target 停止系统。
1, s, single rescue.target 单用户模式。
2, 4 multi-user.target 用户定义/站点特定的运行级别。默认情况下,与 3 相同。
3 multi-user.target 多用户,非图形化。用户通常可以通过多个控制台或通过网络登录。
5 graphical.target 多用户,图形化。通常具有运行级别 3 的所有服务以及图形登录。
6 reboot.target 重启
emergency emergency.target 紧急 Shell

更改当前目标

在 systemd 中,目标通过目标单元公开。您可以像这样更改它们

# systemctl isolate graphical.target

这只会更改当前目标,对下次启动没有影响。这等效于 Sysvinit 中的 telinit 3telinit 5 等命令。

更改默认启动目标

标准目标是 default.target,它是 graphical.target 的符号链接。这大致对应于旧的运行级别 5。

要使用 systemctl 验证当前目标

$ systemctl get-default

要更改默认启动目标,请更改 default.target 符号链接。使用 systemctl

# systemctl set-default multi-user.target
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target -> /usr/lib/systemd/system/multi-user.target.

或者,将以下 内核参数 之一附加到您的启动加载程序

  • systemd.unit=multi-user.target(大致对应于旧的运行级别 3),
  • systemd.unit=rescue.target(大致对应于旧的运行级别 1)。

默认目标顺序

systemd 根据以下顺序选择 default.target

  1. 上面显示的内核参数
  2. /etc/systemd/system/default.target 的符号链接
  3. /usr/lib/systemd/system/default.target 的符号链接

systemd 组件

systemd 的一些(非详尽的)组件是

systemd.mount - 挂载

systemd 负责挂载 /etc/fstab 中指定的分区和文件系统。systemd-fstab-generator(8)/etc/fstab 中的所有条目转换为 systemd 单元;这在启动时以及系统管理器配置重新加载时执行。

systemd 扩展了通常的 fstab 功能,并提供了额外的挂载选项。这些选项会影响挂载单元的依赖关系。例如,它们可以确保仅在网络启动后或另一个分区挂载后才执行挂载。特定 systemd 挂载选项的完整列表(通常以 x-systemd. 为前缀)在 systemd.mount(5) § FSTAB 中详细说明。

这些挂载选项的一个示例是自动挂载,这意味着仅在需要资源时才进行挂载,而不是在启动时自动挂载。这在 fstab#使用 systemd 自动挂载 中提供。

GPT 分区自动挂载

在 UEFI 启动的系统上,GPT 分区(例如 roothomeswap 等)可以按照可发现分区规范自动挂载。因此,这些分区可以从 fstab 中省略,如果 root 分区是自动挂载的,则可以从内核命令行中省略 root=。请参阅 systemd-gpt-auto-generator(8)

前提条件是

udev 将创建一个指向 root 卷块设备的 /dev/gpt-auto-root 符号链接。如果 root 分区使用 LUKS 加密,则 /dev/gpt-auto-root 将指向解锁/映射的卷,而 /dev/gpt-auto-root-luks 将指向加密的分区。

提示: 可以通过更改分区的 类型 GUID 或设置分区属性位 63 “do not automount” 来禁用分区的自动挂载,请参阅 gdisk#阻止 GPT 分区自动挂载
/var

为了使 /var 自动挂载工作,PARTUUID 必须与由机器 ID 键控的分区类型 UUID 的 SHA256 HMAC 哈希匹配。所需的 PARTUUID 可以使用以下命令获取

$ systemd-id128 -u var-partition-uuid
注意: systemd-id128(1)/etc/machine-id 读取机器 ID,这使得在系统安装之前无法知道所需的 PARTUUID。

systemd-sysvcompat

systemd-sysvcompatbase 需要)的主要作用是提供传统的 Linux init 二进制文件。对于 systemd 控制的系统,init 只是其 systemd 可执行文件的符号链接。

此外,它还提供了 SysVinit 用户可能习惯使用的四个便捷快捷方式。便捷快捷方式是 halt(8)poweroff(8)reboot(8)shutdown(8)。这四个命令中的每一个都是 systemctl 的符号链接,并受 systemd 行为的控制。因此,#电源管理 中的讨论也适用。

基于 systemd 的系统可以通过使用 init= 启动参数(例如,请参阅/bin/init is in systemd-sysvcompat ?)和 systemd 原生 systemctl 命令参数来放弃这些 System V 兼容性方法。

systemd-tmpfiles - 临时文件

systemd-tmpfiles 创建、删除和清理易失性和临时文件和目录。它读取 /etc/tmpfiles.d//usr/lib/tmpfiles.d/ 中的配置文件,以发现要执行的操作。前一个目录中的配置文件优先于后一个目录中的配置文件。

配置文件通常与服务文件一起提供,它们的命名风格为 /usr/lib/tmpfiles.d/程序.conf。例如,Samba 守护程序期望目录 /run/samba 存在并具有正确的权限。因此,samba 软件包附带此配置

/usr/lib/tmpfiles.d/samba.conf
D /run/samba 0755 root root

配置文件也可用于在启动时将值写入某些文件。例如,如果您使用 /etc/rc.local 通过 echo USBE > /proc/acpi/wakeup 禁用 USB 设备的唤醒功能,则可以使用以下 tmpfile 代替

/etc/tmpfiles.d/disable-usb-wake.conf
#    Path                  Mode UID  GID  Age Argument
w    /proc/acpi/wakeup     -    -    -    -   USBE

可以将多行写入同一文件,可以使用参数中的 \n 或在多行(包括第一行)上使用 w+ 类型进行追加

/etc/tmpfiles.d/disable-usb-wake.conf
#    Path                  Mode UID  GID  Age Argument
w+   /proc/acpi/wakeup     -    -    -    -   USBE
w+   /proc/acpi/wakeup     -    -    -    -   LID0

有关详细信息,请参阅 systemd-tmpfiles(8)tmpfiles.d(5) 手册页。

注意: 此方法可能不适用于设置 /sys 中的选项,因为 systemd-tmpfiles-setup 服务可能在加载适当的设备模块之前运行。在这种情况下,您可以检查模块是否具有您想要设置的选项的参数,使用 modinfo module 并在 /etc/modprobe.d 中的配置文件中设置此选项。否则,您将必须编写一个 udev 规则,以便在设备出现时立即设置相应的属性。


Drop-in 配置文件

本文或本节的事实准确性存在争议。

原因: 此页面是关于 PID 1 (init) 的,并且已经提到了单元 drop-in。Drop-in 是一个通用概念,加上其他 systemd 组件都有其专门的 wiki 页面。因此,本节似乎不属于此处。(在Talk:Systemd#YHNdnzj : Configuration files in conf.d / drop-in snippets: misplaced?中讨论)

不应直接编辑软件包提供的配置文件,以避免与 pacman 更新发生冲突。为此,许多(但不是全部)systemd 软件包提供了一种修改配置的方法,但无需通过创建 drop-in 代码段来触及原始文件。检查软件包手册以查看是否支持 drop-in 配置文件。

要为单元文件 /etc/systemd/unit.conf 创建 drop-in 配置文件,请创建目录 /etc/systemd/unit.conf.d/,并将 .conf 文件放在此处以覆盖或添加新选项。systemd 将解析这些文件并将它们应用到原始单元之上。

检查整体配置

$ systemd-analyze cat-config systemd/unit.conf

应用的 drop-in 代码段文件及其内容将列在末尾。重启服务以使更改生效。

技巧和窍门

套接字激活

某些软件包提供 .socket 单元。例如,cups 提供了一个 cups.socket 单元[3]。如果 cups.socket启用(并且 cups.service 保持禁用状态),systemd 将不会立即启动 CUPS;它只会监听相应的套接字。然后,每当程序尝试连接到这些 CUPS 套接字之一时,systemd 将启动 cups.service 并透明地将这些端口的控制权移交给 CUPS 进程。

GUI 配置工具

  • systemadmsystemd 单元的图形浏览器。它可以显示单元列表,并可以按类型过滤。
https://cgit.freedesktop.org/systemd/systemd-ui/ || systemd-ui
  • SystemdGenie — 基于 KDE 技术的 systemd 管理实用程序。
https://invent.kde.org/system/systemdgenie || systemdgenie

在网络启动后运行服务

要延迟服务直到网络启动后,请在 .service 文件中包含以下依赖项

/etc/systemd/system/foo.service
[Unit]
...
Wants=network-online.target
After=network-online.target
...

还必须启用正在使用的网络管理器的网络等待服务,以便 network-online.target 正确反映网络状态。

  • 如果使用 NetworkManager,则应同时启用 NetworkManager-wait-online.serviceNetworkManager.service。使用 systemctl is-enabled NetworkManager-wait-online.service 检查是否是这种情况。如果未启用,则重新启用 NetworkManager.service
  • 对于 netctl启用 netctl-wait-online.service(除非您正在使用 netctl-auto;请参阅 FS#75836)。
  • 如果使用 systemd-networkd,则应同时启用 systemd-networkd-wait-online.servicesystemd-networkd.service。使用 systemctl is-enabled systemd-networkd-wait-online.service 检查是否是这种情况。如果未启用,则重新启用 systemd-networkd.service

有关更详细的说明,请参阅网络配置同步点中的讨论。

如果服务需要执行 DNS 查询,则还应在 nss-lookup.target 之后排序

/etc/systemd/system/foo.service
[Unit]
...
Wants=network-online.target
After=network-online.target nss-lookup.target
...

请参阅 systemd.special(7) § 特殊被动系统单元

为了使 nss-lookup.target 生效,它需要一个通过 Wants=nss-lookup.target 拉入它并通过 Before=nss-lookup.target 将自身排序在其之前的服务。通常,这是由本地DNS 解析器完成的。

使用以下命令检查是否有任何活动服务正在拉入 nss-lookup.target

$ systemctl list-dependencies --reverse nss-lookup.target

默认情况下启用已安装的单元

本文或本节需要扩充。

原因: 它如何与实例化的单元一起工作?(在Talk:Systemd中讨论)

Arch Linux 附带 /usr/lib/systemd/system-preset/99-default.preset,其中包含 disable *。这会导致 systemctl preset 默认禁用所有单元,这样,当安装新软件包时,用户必须手动启用该单元。

如果不希望这样做,只需从 /etc/systemd/system-preset/99-default.preset 创建一个到 /dev/null 的符号链接即可覆盖配置文件。这将导致 systemctl preset 启用所有已安装的单元(无论单元类型如何),除非在systemctl preset 的配置目录中的另一个文件中指定。用户单元不受影响。有关更多信息,请参阅 systemd.preset(5)

注意: 默认情况下启用所有单元可能会导致包含两个或多个互斥单元的软件包出现问题。systemctl preset 旨在供发行版和衍生版或系统管理员使用。如果将启用两个冲突的单元,则应在预设配置文件中显式指定要禁用的单元,如 systemd.preset(5) 手册页中所述。

沙盒化应用程序环境

本文或本节是移动到 systemd/沙盒化 的候选对象。

注释: 该主题足够广泛,可以创建一个专门的页面。请参阅 User:NetSysFire/systemd sandboxing 以获取拟议的草案。(在Talk:Security#systemd unit hardening and system.conf tweaks中讨论)

可以创建一个单元文件作为沙盒,以在强化的虚拟环境中隔离应用程序及其进程。systemd 利用 命名空间、允许/拒绝的 capabilities 列表和 控制组,通过广泛的执行环境配置(systemd.exec(5))来容器化进程。

使用应用程序沙盒增强现有 systemd 单元文件通常需要反复试验测试,并慷慨使用 stracestderrjournalctl(1) 错误日志记录和输出工具。您可能需要先搜索上游文档,以查找已完成的测试作为试验的基础。要获得可能的强化选项的起点,请运行

$ systemd-analyze security unit

使用 systemd 部署沙盒的一些示例

通知服务失败

为了通知服务失败,需要将 OnFailure= 指令添加到相应的服务文件中,例如通过使用drop-in 配置文件。可以使用顶层 drop-in 配置文件将此指令添加到每个服务单元。有关顶层 drop-in 的详细信息,请参阅 systemd.unit(5)

为服务创建顶层 drop-in

/etc/systemd/system/service.d/toplevel-override.conf
[Unit]
OnFailure=failure-notification@%n.service

这会将 OnFailure=failure-notification@%n.service 添加到每个服务文件。如果 some_service_unit 失败,将启动 failure-notification@some_service_unit.service 以处理通知传递(或配置为执行的任何任务)。

创建 failure-notification@.service 模板单元

/etc/systemd/system/failure-notification@.service
[Unit]
Description=Send a notification about a failed systemd unit

[Service]
Type=oneshot
ExecStart=/path/to/failure-notification.sh %i
# runs as a temporary user/group and enables several other security precautions
DynamicUser=true

您可以创建 failure-notification.sh 脚本并定义要执行的操作或如何通知。示例包括发送电子邮件显示桌面通知、使用 gotify、XMPP 等。%i 将是失败的服务单元的名称,并将作为参数传递给脚本。

为了防止再次重复启动 failure-notification@.service 实例的递归(如果启动失败),请创建一个与顶层 drop-in 同名的空 drop-in 配置文件(空的服务级 drop-in 配置文件优先于顶层 drop-in 并覆盖后者)

# mkdir -p /etc/systemd/system/failure-notification@.service.d
# touch /etc/systemd/system/failure-notification@.service.d/toplevel-override.conf

通过电子邮件通知

您可以设置 systemd 在单元失败时发送电子邮件。如果作业输出到 stdout 或 stderr,Cron 会将邮件发送到 MAILTO,但许多作业都设置为仅在出错时输出。首先,您需要两个文件:一个用于发送邮件的可执行文件和一个用于启动可执行文件的 .service 文件。对于此示例,可执行文件只是一个使用 sendmail 的 shell 脚本,该脚本位于提供 smtp-forwarder 的软件包中。

/usr/local/bin/systemd-email
#!/bin/sh

/usr/bin/sendmail -t <<ERRMAIL
To: $1
From: systemd <root@$HOSTNAME>
Subject: $2
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8

$(systemctl status --full "$2")
ERRMAIL

无论您使用什么可执行文件,它都可能应该至少接受两个参数,如此 shell 脚本一样:要发送到的地址和要获取其状态的单元文件。我们创建的 .service 将传递这些参数

/etc/systemd/system/status_email_user@.service
[Unit]
Description=status email for %i to user

[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-email address %i
User=nobody
Group=systemd-journal

其中 user 是被电子邮件的用户,address 是该用户的电子邮件地址。尽管收件人是硬编码的,但要报告的单元文件是作为实例参数传递的,因此这项服务可以为许多其他单元发送电子邮件。此时,您可以启动 status_email_user@dbus.service 以验证您是否可以收到电子邮件。

然后只需编辑您想要接收电子邮件的服务,并将 OnFailure=status_email_user@%n.service 添加到 [Unit] 部分。%n 将单元的名称传递给模板。

注意
  • 如果您按照 sSMTP#安全性 设置了 sSMTP 安全性,则用户 nobody 将无法访问 /etc/ssmtp/ssmtp.conf,并且 systemctl start status_email_user@dbus.service 命令将失败。一种解决方案是在 status_email_user@.service 单元中使用 root 作为 User。
  • 如果您尝试在您的电子邮件脚本中使用 mail -s somelogs addressmail 将 fork,当 systemd 看到您的脚本退出时,它将杀死 mail 进程。通过执行 mail -Ssendwait -s somelogs address 使 mail 非 fork。
提示: 较新版本的 systemd 建议使用 DynamicUser=true 作为 User=nobody 的替代品,现在不鼓励使用后者。有关更多详细信息,请参阅 GitHub issue 428

在关机时自动关闭外部 HDD

请参阅 udisks#在关机时自动关闭外部 HDD

故障排除

调查失败的服务

查找未能启动的 systemd 服务

$ systemctl --state=failed

要查明它们失败的原因,请检查其日志输出。有关详细信息,请参阅 systemd/Journal#过滤输出

诊断启动问题

systemd 具有多个选项,用于诊断启动过程中的问题。有关更通用的说明和在 systemd 接管启动过程之前捕获启动消息的选项,请参阅 启动调试。另请参阅 systemd 调试文档

诊断服务

如果某些 systemd 服务行为不当,或者您想获得有关正在发生的事情的更多信息,请将 SYSTEMD_LOG_LEVEL 环境变量设置为 debug。例如,要在调试模式下运行 systemd-networkd 守护程序

为服务添加一个 drop-in 文件,添加以下两行

[Service]
Environment=SYSTEMD_LOG_LEVEL=debug

或者,等效地,手动设置环境变量

# SYSTEMD_LOG_LEVEL=debug /lib/systemd/systemd-networkd

然后重启 systemd-networkd,并使用 -f/--follow 选项监视服务的日志。

关机/重启时间过长

如果关机过程花费很长时间(或似乎冻结),则很可能是服务未退出造成的。systemd 会等待一段时间让每个服务退出,然后再尝试杀死它。要查明您是否受到影响,请参阅 systemd 文档中的 Shutdown completes eventually

常见问题是关机或挂起进程停滞。要验证是否是这种情况,您可以运行以下任一命令并检查输出

# systemctl poweroff
Failed to power off system via logind: There's already a shutdown or sleep operation in progress
# systemctl list-jobs
  JOB UNIT                    TYPE  STATE  
...
21593 systemd-suspend.service start running
21592 suspend.target          start waiting
..

此问题的解决方案是运行以下命令取消这些作业

# systemctl cancel
# systemctl stop systemd-suspend.service

然后再次尝试关机或重启。

短时进程似乎没有记录任何输出

如果以 root 身份运行 journalctl -u foounit 没有显示任何短时服务的输出,请查看 PID。例如,如果 systemd-modules-load.service 失败,并且 systemctl status systemd-modules-load 显示它以 PID 123 运行,那么您可能能够在日志中看到该 PID 的输出,即以 root 身份运行 journalctl -b _PID=123。日志的元数据字段(例如 _SYSTEMD_UNIT_COMM)是异步收集的,并且依赖于 /proc 目录以获取进程存在的信息。修复此问题需要修复内核以通过套接字连接提供此数据,类似于 SCM_CREDENTIALS。简而言之,这是一个 bug。请记住,根据 systemd 的设计,立即失败的服务可能不会向日志打印任何内容。

启动时间随时间推移而增加

本文或本节的事实准确性存在争议。

原因: NetworkManager 问题不是 systemd 的故障,所谓的报告缺失。缓慢的 systemctl statusjournalctl 不会影响启动时间。(在Talk:Systemd中讨论)

在使用 systemd-analyze 后,许多用户注意到他们的启动时间与过去相比显着增加。在使用 systemd-analyze blame 后,NetworkManager 被报告为花费异常多的时间来启动。

某些用户的问题是由于 /var/log/journal 变得太大。这可能会对性能产生其他影响,例如对于 systemctl statusjournalctl。因此,解决方案是删除文件夹中的每个文件(理想情况下,至少暂时备份到某个地方),然后按照 Systemd/Journal#日志大小限制 中所述设置日志文件大小限制。

systemd-tmpfiles-setup.service 启动时失败

systemd 219 开始,/usr/lib/tmpfiles.d/systemd.conf 指定了 /var/log/journal 下目录的 ACL 属性,因此,需要为日志所在的文件系统启用 ACL 支持。

有关如何在容纳 /var/log/journal 的文件系统上启用 ACL 的说明,请参阅 访问控制列表#启用 ACL

禁用远程计算机上的紧急模式

您可能想要禁用远程计算机上的紧急模式,例如,Azure 或 Google Cloud 上托管的虚拟机。这是因为如果触发紧急模式,机器将被阻止连接到网络。

要禁用它,屏蔽 emergency.serviceemergency.target

错误“Unit xxx.service not found”,但服务确实存在

您可能正在尝试将用户单元作为系统单元启动或启用。systemd.unit(5) 指出哪些单元位于何处。默认情况下,systemctl 对系统服务进行操作。

有关更多详细信息,请参阅 Systemd/User

另请参阅