跳转至内容

systemd

来自 ArchWiki
(重定向自 Tmpfile)

来自 项目网页

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

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

注意: 关于 Arch 为何转向 systemd 的详细解释,请参见 这篇论坛帖子

systemctl 基本用法

用于检查和控制 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 的隐含默认选项。若要操作(针对调用用户的)用户单元,请在没有 root 权限的情况下使用 systemctl --user。另请参见 systemd/User#基本设置 以针对所有用户启用/禁用用户单元。

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

电源 management

作为非特权用户进行电源管理需要 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 文件,而后者又受到 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 启动之前运行。在这种情况下,在 A[Unit] 部分添加 Requires=BAfter=B。如果依赖关系是可选的,请改用 Wants=BAfter=B。注意 Wants=Requires= 并不包含 After=,这意味着如果不指定 After=,两个单元将并行启动。

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

服务类型

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

  • Type=simple (默认):systemd 认为服务立即启动。进程不得 fork。除非是套接字激活,否则如果其他服务需要依赖此服务,请勿使用此类型。
  • Type=forking:一旦进程 fork 且父进程退出,systemd 就认为服务已启动。对于传统的守护进程,除非您确定不需要,否则请使用此类型。您还应该指定 PIDFile=,以便 systemd 跟踪主进程。
  • Type=oneshot:适用于完成单一任务后退出的脚本。您可能还想设置 RemainAfterExit=yes,以便 systemd 在进程退出后仍认为服务处于活动状态。设置 RemainAfterExit=yes 适用于改变系统状态的单元(例如,挂载某些分区)。关于 simple 和 oneshot 的区别,另见 [1]
  • Type=notify:与 Type=simple 相同,但约定守护进程在准备就绪时会向 systemd 发送信号。此通知的参考实现由 libsystemd-daemon.so 提供。
  • Type=dbus:当指定的 BusName 出现在 DBus 系统总线上时,服务被认为已就绪。
  • Type=idlesystemd 将延迟执行服务二进制文件,直到所有作业都分派完毕,除非这超过 5 秒。除此之外,其行为与 Type=simple 非常相似。它绝不应由于服务排序,旨在帮助提高控制台输出的可读性。

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

编辑现有单元

本文章或章节需要扩充。

理由: 我们应该在此处加入“如果指定了 --runtime,更改将临时在 /run/ 中进行,并在下次重启时丢失。”。一个例子是向服务传递调试标志且不想永久保留更改。(在 Talk:Systemd 中讨论)

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

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

替换单元文件

要替换单元文件 /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 创建补充文件,请创建目录 /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

  • 键 (key) 仍必须放置在覆盖文件中相应的节 (section) 下。
  • 并非所有的键都能通过补充文件覆盖。例如,要更改 Conflicts=必须使用替换文件。
  • 您可以使用顶层补充文件来影响所有相同类型的单元。例如,/etc/systemd/system/service.d/ 中的补充文件会影响所有 .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] 部分设置 0 到 6 之间的数值给 LogLevelMax= 参数来控制其详细程度。例如:

/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

目标 (Targets)

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

获取当前目标

systemd 下应使用以下命令,而不是运行 runlevel

$ systemctl list-units --type=target

创建自定义目标

在 sysvinit 下具有定义含义的运行级别(即 0、1、3、5 和 6)与特定的 systemd 目标有一一对应的映射。不幸的是,对于用户定义的运行级别(如 2 和 4),没有很好的对应方法。如果您使用这些级别,建议您在 /etc/systemd/system/your target 创建一个新的命名 systemd 目标,以现有的运行级别之一作为基础(您可以参考 /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 中,目标通过目标单元 (target units) 暴露。您可以像这样更改它们:

# 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 引导的系统上,遵循 可发现分区规范 (Discoverable Partitions Specification),可以自动挂载 GPT 分区,如 roothomeswap 等。因此,这些分区可以从 fstab 中省略,如果根分区被自动挂载,那么内核命令行中也可以省略 root=。参见 systemd-gpt-auto-generator(8)

前提条件如下:

udev 将创建 指向发现的分区的符号链接,可用于在配置文件中引用分区和卷。

提示: 可以通过更改分区的 类型 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 行为的管辖。因此,#Power management 中的讨论适用。

基于 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/program.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 规则,以便在设备出现后立即设置相应的属性。


补充配置文件

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

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

软件包提供的配置文件不应直接编辑,以避免与 pacman 更新发生冲突。为此,许多(但不是全部)systemd 软件包提供了一种修改配置的方法,即通过创建补充片段而不触动原始文件。请查看软件包手册以了解是否支持补充配置文件。

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

检查整体配置:

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

应用的补充片段文件及其内容将列在最后。重启服务以使更改生效。

技巧与提示

套接字激活 (Socket activation)

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

GUI 配置工具

  • systemadmsystemd 单元的图形浏览器。它可以显示单元列表,并可按类型过滤。
https://github.com/systemd/systemd-ui || systemd-ui
  • systemdGenie — 基于 KDE 技术的 systemd 管理工具。
https://apps.kde.org/systemdgenie/ || systemdgenie
  • isd — 用于操作 systemd 单元的 TUI(文本用户界面)。
https://github.com/kainctl/isd || isd

在网络就绪后运行服务

要将服务延迟到网络就绪之后,请在 .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) § Special Passive System Units

为了使 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 不使用 systemd 预设 (presets),并且在安装时不启用大多数服务。

如果您希望在安装软件包时遵循 systemd 预设,则需要创建一个 pacman 钩子,以便在安装新的 systemd 单元时运行 systemctl presetsystemctl preset-all。类似于:

/etc/pacman.d/hooks/40-systemd-presets.hook
[Trigger]
Operation = Install
Type = Path
Target = usr/lib/systemd/system/*.service
Target = usr/lib/systemd/system/*.timer
Target = usr/lib/systemd/system/*.socket
Target = usr/lib/systemd/system/*.slice
Target = usr/lib/systemd/system/*.path
Target = usr/lib/systemd/system/*.target

[Action]
Description = Run systemctl preset
When = PostTransaction
Depends = systemd
NeedsTargets
# Alternatively, you could just run systemd preset-all.
# However, note that this will reset ALL services to the defaults,
# which is probably not what you want unless you have carefully configured
# your presets.
Exec = /etc/pacman.d/scripts/systemd-presets-hook
/etc/pacman.d/scripts/systemd-presets-hook
#!/bin/sh -e

while read -r f; do
    unit="${f##*/}"
    # alternatively, you could read all the units and pass them to systemctl preset in a single invocation
    systemctl preset "$unit"
done

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

若要改为默认启用单元,只需创建一个从 /etc/systemd/system-preset/99-default.preset 指向 /dev/null 的符号链接,或用包含 enable * 的文件替换它以覆盖配置文件。这将导致 systemctl preset 启用所有安装的单元(无论单元类型),除非在 systemctl preset 的另一个配置目录下的文件中另有指定。用户单元不受影响。还可以配置具有更高优先级的更具体规则的文件来控制应启用哪些单元。更多信息请参见 systemd.preset(5)

注意: 默认启用所有单元可能会对包含两个或多个互斥单元的软件包造成问题。systemctl preset 旨在供发行版、衍生版或系统管理员使用。在两个冲突单元都将被启用的情况下,您应按照 systemd.preset(5) 手册页中的规定,在预设配置文件中显式指定应禁用哪一个。

沙箱化应用程序环境

参见 systemd/Sandboxing

失败服务通知

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

为服务创建顶层补充文件:

/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 实例的情况,请创建一个与顶层补充文件同名的空补充配置文件(空的服务级补充文件优先级高于顶层补充文件并会覆盖后者):

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

邮件通知

您可以设置 systemd 在单元失败时发送电子邮件。Cron 会在作业向 stdout 或 stderr 输出时向 MAILTO 发送邮件,但许多作业被设置为仅在出错时输出。首先,您需要两个文件:一个用于发送邮件的可执行文件和一个用于启动该可执行文件的 .service。在本例中,可执行文件只是一个使用 sendmail 的 shell 脚本,sendmail 包含在提供 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 以验证您可以收到电子邮件。

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

  • 如果您根据 sSMTP#Security 设置了 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 -Ssendwait -s somelogs address 使邮件保持非 fork 状态。
提示: 较新版本的 systemd 建议使用 DynamicUser=true 作为 User=nobody 的替代方案,现在已不鼓励使用后者。详情请参见 GitHub issue 428

关机时自动关闭外部硬盘

参见 udisks#关机时自动关闭外部硬盘

故障排除

调查失败的服务

要查找启动失败的 systemd 服务:

$ systemctl --failed

要找出它们失败的原因,请检查其日志输出。详情请参见 systemd/Journal#过滤输出

诊断引导问题

systemd 有多个诊断引导过程问题的选项。有关在 systemd 接管 引导过程 之前捕获引导消息的更多常规说明和选项,请参见 引导调试。另请参见 systemd 调试文档

诊断服务

如果某个 systemd 服务表现异常,或者您想获取有关正在发生的事情的更多信息,请将 SYSTEMD_LOG_LEVEL 环境变量 设置为 debug。例如,要在调试模式下运行 systemd-networkd 守护进程:

为该服务添加一个 补充文件,添加以下两行:

[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)#启用 ACL

禁用远程机器的紧急模式

您可能希望在远程机器上禁用紧急模式,例如托管在 Azure 或 Google Cloud 的虚拟机。这是因为如果触发了紧急模式,机器将无法连接到网络。

要禁用它,请掩蔽 emergency.serviceemergency.target

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

您可能正尝试将用户单元作为系统单元启动或启用。systemd.unit(5) 指出了单元文件的存放位置。默认情况下 systemctl 操作的是系统服务。

详情请参见 systemd/User

更改 EFI 分区 UUID 后手动更新 LoaderDevicePartUUID

某些引导加载程序仅在 LoaderDevicePartUUID 变量为空时才设置它。因此,即使 EFI 分区的 UUID 发生更改,引导加载程序也不会更新 LoaderDevicePartUUID。通过使用以下命令删除该 EFI 变量,引导加载程序随后会使用新的 UUID 重新填充它。

# chattr -i /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
# rm /sys/firmware/efi/efivars/LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f

参见