systemd

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

来自 项目网页

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

从历史上看,systemd 称之为 “service” 的东西曾被命名为 daemon:任何作为 “后台” 进程(没有终端或用户界面)运行的程序,通常等待事件发生并提供服务。一个很好的例子是 Web 服务器,它等待请求以交付页面,或者 ssh 服务器,它等待有人尝试登录。虽然这些是功能齐全的应用程序,但有些守护进程的工作不太明显。守护进程用于将消息写入日志文件(例如 syslogmetalog)或保持系统时间准确(例如 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#Basic setup 以启用/禁用所有用户的用户单元。

提示
  • 如果指定了多个单元,大多数命令也有效,有关更多信息,请参见 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 切片、内存和父进程
检查单元状态
显示与单元关联的手册页 systemctl help unit 由单元支持
单元的 状态 systemctl status unit 包括它是否正在运行
检查单元是否已启用 systemctl is-enabled unit
启动、重启、重新加载单元
立即启动单元 systemctl start unit as root
立即停止单元 systemctl stop unit as root
重启单元 systemctl restart unit as root
重新加载单元及其配置 systemctl reload unit as root
重新加载 systemd 管理器配置2 systemctl daemon-reload as root 扫描新的或已更改的单元
启用单元
启用单元以在启动时自动启动 systemctl enable unit as root
启用单元以在启动时自动启动并立即启动 systemctl enable --now unit as root
禁用单元使其不再在启动时启动 systemctl disable unit as root
重新启用单元3 systemctl reenable unit as root 即禁用并重新启用
屏蔽单元
屏蔽单元使其无法启动4 systemctl mask unit as root
取消屏蔽单元 systemctl unmask unit as 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=,则两个单元将并行启动。

依赖关系通常放在服务上,而不是 #目标 (Targets) 上。例如,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

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

抑制服务的标准输出

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

目标 (Targets)

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

获取当前目标 (targets)

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

$ systemctl list-units --type=target

创建自定义目标 (target)

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

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

SysV 运行级别 systemd 目标 (Target) 注释
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

更改当前目标 (target)

systemd 中,目标 (targets) 通过目标单元 (target units) 公开。你可以像这样更改它们:

# systemctl isolate graphical.target

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

更改默认启动目标 (target)

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

要使用 systemctl 验证当前目标 (target):

$ systemctl get-default

要更改默认启动目标 (target),请更改 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)。

默认目标 (target) 顺序

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=。请参阅 systemd-gpt-auto-generator(8)

先决条件是

udev 将创建一个指向根卷块设备的 /dev/gpt-auto-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/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 规则,以便在设备出现时立即设置适当的属性。


Drop-in 配置文件

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

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

不应直接编辑软件包提供的配置文件,以避免与 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/Sandboxing 的候选对象。

注意: 该主题足够广泛,可以专门创建一个页面。有关建议的草案,请参阅 User:NetSysFire/systemd sandboxing。(在 Talk:Security#systemd 单元强化和 system.conf 调整 中讨论)

可以创建一个单元文件作为沙箱,以在强化的虚拟环境中隔离应用程序及其进程。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 作为用户。
  • 如果您尝试在电子邮件脚本中使用 mail -s somelogs addressmail 将 fork,并且当 systemd 看到您的脚本退出时,它将杀死 mail 进程。通过执行 mail -Ssendwait -s somelogs address 使 mail 成为非 fork 的。
提示: 较新版本的 systemd 建议使用 DynamicUser=true 作为 User=nobody 的替代品,现在不鼓励使用 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 的 fault,所谓的报告缺失。缓慢的 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

另请参阅