跳到内容跳到页面导航:上一页 [access key p]/下一页 [access key n]
documentation.suse.com / SUSE Linux Enterprise Server 文档 / 管理指南 / 引导 Linux 系统 / systemd 守护程序
适用范围 SUSE Linux Enterprise Server 15 SP6

19 systemd 守护程序

systemd 用于初始化系统。它的进程 ID 为 1。systemd 由内核直接启动,信号 9(通常会终止进程)对它不起作用。所有其他程序要么由 systemd 直接启动,要么由它的其中一个子进程启动。systemd 取代了 System V init 守护程序,并且通过支持 init 脚本与 System V init 完全兼容。

systemd 的主要优点是通过并行化服务启动,大大加快了引导速度。另外,systemd 只在确实有需要时才会启动服务。即不会在系统引导时无条件启动守护程序,而是在第一次需要时才会启动。systemd 还支持内核控制组 (cgroup)、创建快照和恢复系统状态。有关更多细节,请参见 https://www.freedesktop.org/wiki/Software/systemd/

提示
提示:WSL 中的 systemd

适用于 Linux 的 Windows 子系统 (WSL) 允许在 Microsoft Windows 操作系统下运行 Linux 应用程序和发行套件。WSL 使用其 init 进程,而不是 systemd。要在 WSL 中运行的 SLES 中启用 systemd,请安装自动执行该过程的 wsl_systemd 软件集:

> sudo zypper in -t pattern wsl_systemd

或者,您可以手动编辑 /etc/wsl.conf 并添加以下几行内容:

[boot]
systemd=true

请注意,WSL 中仅对 systemd 提供部分支持,即 systemd 单元文件必须具有合理的进程管理行为。

19.1 systemd 概念

下面的部分解释了 systemd 背后的概念。

systemd 是适用于 Linux 的系统和会话管理器,它与 System V 及 LSB init 脚本兼容。systemd 的主要功能包括:

  • 并行化能力

  • 通过套接字及 D-Bus 激活来启动服务

  • 按需启动守护程序

  • 使用 Linux cgroup 跟踪进程

  • 创建快照和恢复系统状态

  • 维护安装点和自动安装点

  • 实施精细的、基于事务依赖性的服务控制逻辑

19.1.1 单元文件

单元配置文件包含有关以下项目的信息:服务、套接字、设备、挂载点、自动挂载点、交换文件或分区、启动目标、监控的文件系统路径、受 systemd 控制和监管的计时器、临时系统状态快照、资源管理部分或一组从外部创建的进程。

单元文件systemd 用于描述下列各项的通用术语:

  • 服务: 进程相关信息(例如,运行守护程序);文件名以 .service 结尾

  • 目标: 用于将单元分组以及在启动期间用作同步点;文件以 .target 结尾

  • 套接字: IPC 或网络套接字或文件系统 FIFO 相关信息,适用于基于套接字的激活(如 inetd);文件以 .socket 结尾

  • 路径: 用于触发其他单元(例如,在文件更改后运行某个服务);文件名以 .path 结尾

  • 计时器: 受控制计时器相关信息,适用于基于计时器的激活;文件以 .timer 结尾

  • 挂载点: 通常由 fstab 生成器自动生成;文件名以 .mount 结尾

  • 自动挂载点: 文件系统自动挂载点相关信息;文件以 .automount 结尾

  • 交换: 内存分页的交换设备或文件相关信息;文件以 .swap 结尾

  • 设备: sysfs/udev(7) 设备树中公开的设备相关信息;文件以 .device 结尾

  • 范围/部分: 有关分层管理一组进程的资源的概念;文件以 .scope/.slice 结尾

有关 systemd 单元文件的详细信息,请参见 https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html

19.2 基本用法

System V init 系统使用多个命令来处理服务:init 脚本、insservtelinit 及其他。systemd 使服务管理变得更加容易,因为您只需使用一个命令即可处理大多数与服务相关的任务,即 systemctl 命令。它使用命令加子命令表示法,与 gitzypper 相似:

systemctl GENERAL OPTIONS SUBCOMMAND SUBCOMMAND OPTIONS

有关完整的手册,请参见 man 1 systemctl

提示
提示:终端输出和 Bash 补全

如果输出传递到某个终端(而不是某个管道或文件等),systemd 命令默认会将长输出发送到分页器。使用 --no-pager 选项可关闭分页模式。

systemd 还支持 bash 补全,这样您只需输入子命令的头几个字母,然后按 →| 即可。只能在 bash 外壳中使用此功能,并且需要安装 bash-completion 软件包。

19.2.1 管理正在运行的系统中的服务

用于管理服务的子命令与使用 System V init 管理服务的子命令相同(startstop 等)。服务管理命令的一般语法如下所示:

systemd
systemctl reload|restart|start|status|stop|... MY_SERVICE(S)
System V init
rcMY_SERVICE(S) reload|restart|start|status|stop|...

systemd 允许您一次管理多个服务。不用像 System V init 要逐个执行 init 脚本,而是执行如下命令:

> sudo systemctl start MY_1ST_SERVICE MY_2ND_SERVICE

要列出系统上所有可用的服务,请运行:

> sudo systemctl list-unit-files --type=service

下表列出了 systemd 和 System V init 最重要的服务管理命令:

表 19.1︰ 服务管理命令

任务

systemd 命令

System V init 命令

启动:

start
start

停止:

stop
stop

重启动: 关闭服务,然后再启动这些服务。如果某项服务尚未运行,则会將其启动。

restart
restart

有条件地重启动: 如果服务当前正在运行,则重启动它们。对未在运行的服务不执行任何操作。

try-restart
try-restart

重新加载: 让服务在不中断操作的情况下重新加载它们的配置文件。使用案例:让 Apache 重新加载修改过的 httpd.conf 配置文件。并非所有服务都支持重新加载。

reload
reload

重新加载或重启动: 如果支持重新加载则重新加载服务,否则重启动服务。如果某项服务尚未运行,则會將其启动。

reload-or-restart
n/a

有条件地重新加载或重启动: 如果支持重新加载则重新加载服务,否则,如果服务当前正在运行,则重启动服务。对未在运行的服务不执行任何操作。

reload-or-try-restart
n/a

获得详细的状态信息: 列出服务状态的相关信息。systemd 命令会显示细节,例如说明、可执行文件、状态、cgroup 及服务发出的最新消息(请参见第 19.6.9 节 “调试服务”)。使用 System V init 显示的详细程度因服务而异。

status
status

获得简要的状态信息: 显示服务是否处于活动状态。

is-active
status

19.2.2 永久启用/禁用服务

上一节中提到的服务管理命令可让您操控当前会话的服务。systemd 还允许您永久启用或禁用服务,让它们在用户要求时自动启动或永远不可用。您可以使用 YaST 或在命令行上运行命令来实现此目的。

19.2.2.1 在命令行上启用/禁用服务

下表列出了 systemd 和 System V init 用于启用和禁用服务的命令:

重要
重要:服务启动

在命令行上启用服务时,服务不会自动启动。它会安排在下一次系统启动或运行级别/目标发生更改时启动。要在启用服务之后立即启动它,请明确运行 systemctl start MY_SERVICErc MY_SERVICE start

表 19.2︰ 用于启用和禁用服务的命令

任务

systemd 命令

System V init 命令

启用:

systemctl enable MY_SERVICE(S)

insserv MY_SERVICE(S),​chkconfig -a MY_SERVICE(S)

禁用:

systemctl disable MY_SERVICE(S).service

insserv -r MY_SERVICE(S),​chkconfig -d MY_SERVICE(S)

检查: 显示某项服务是否启用。

systemctl is-enabled MY_SERVICE

chkconfig MY_SERVICE

重新启用: 与重启动服务相似,此命令先禁用服务,然后再启用该服务。可用来使用默认值重新启用服务。

systemctl reenable MY_SERVICE

屏蔽: 禁用某项服务之后,仍然可以手动启动它。要禁用服务,您需要将其屏蔽。须谨慎使用该功能。

systemctl mask MY_SERVICE

取消屏蔽: 屏蔽某项服务之后,只有在将其取消屏蔽之后才能再次使用它。

systemctl unmask MY_SERVICE

19.3 系统启动和目标管理

启动和关闭系统的整个过程由 systemd 负责维护。从这一点来看,可以将内核视为一个后台进程,其任务是维护所有其他进程,以及根据其他程序的请求来调整 CPU 时间和硬件访问。

19.3.1 目标与运行级别的比较

使用 System V init 时,系统引导到所谓的运行级别。运行级别定义系统如何启动以及正在运行的系统中有哪些服务可用。运行级别是有编号的;最知名的运行级别是 0(关闭系统)、3(联网的多用户模式)和 5(联网并使用显示管理器的多用户模式)。

systemd 使用所谓的目标单元引入新的概念。不过,它仍然与运行级别概念完全兼容。目标单元用名称而不是编号来标识,并具有特定的作用。例如,目标 local-fs.targetswap.target 會挂载本地文件系统和交换空间。

目标 graphical.target 提供联网并使用显示管理器的多用户系统,作用与运行级别 5 相当。复杂目标(如 graphical.target)通过将一部分其他目标组合起来充当目标。systemd 通过组合现有目标简化了创建自定义目标的工作,从而提供了极大的灵活性。

下面的列表显示了最重要的 systemd 目标单元。有关完整列表,请参见 man 7 systemd.special

选定的 systemd 目标单元
default.target

默认引导的目标。这并不是实际目标,而是指向另一个目标(如 graphic.target)的符号链接,可通过 YaST 永久更改(请参见第 19.4 节 “使用 YaST 管理服务”)。要为某个会话更改它,请在引导提示处使用内核参数 systemd.unit=MY_TARGET.target

emergency.target

在控制台上启动极简紧急 root 外壳。请仅在引导提示处按以下格式使用它:systemd.unit=emergency.target

graphical.target

启动联网的具有多用户支持和显示管理器的系统。

halt.target

关闭系统。

mail-transfer-agent.target

启动发送和接收邮件所需的所有服务。

multi-user.target

启动联网的多用户系统。

reboot.target

重新引导系统。

rescue.target

启动不联网的单用户 root 会话。我们提了基本的系统管理工具。该 rescue 目标适用于解决多种系统问题,例如,登录失败或修复与显示驱动程序有关的问题。

为了与 System V init 运行级别系统保持兼容,systemd 提供了名为 runlevelX.target 的特殊目标,与编号为 X 的相应运行级别相对应。

要检查当前目标,请使用命令 systemctl get-default

表 19.3︰ System V 运行级别和 systemd 目标单元

System V 运行级别

systemd target

用途

0

runlevel0.target、​halt.target、​poweroff.target

系统关闭

1、S

runlevel1.target、​rescue.target

单用户模式

2

runlevel2.target、​multi-user.target

无远程联网的本地多用户模式

3

runlevel3.target、​multi-user.target

完整的联网多用户模式

4

runlevel4.target

未使用/用户定义

5

runlevel5.target、​graphical.target

联网并使用显示管理器的完整多用户模式

6

runlevel6.target、​reboot.target

系统重引导

重要
重要:systemd 会忽略 /etc/inittab

System V init 系统中的运行级别在 /etc/inittab 中配置。systemd 使用此配置。有关如何创建您自己的可引导目标的说明,请参考第 19.5.5 节 “创建自定义目标”

19.3.1.1 用于更改目标的命令

可使用下列命令来操作目标单元:

任务

systemd 命令

System V init 命令

更改当前目标/运行级别

systemctl isolate MY_TARGET.target

telinit X

更改为默认目标/运行级别

systemctl default

获得当前目标/运行级别

systemctl list-units --type=target

对于 systemd,一般会有多个活动目标。该命令可列出当前处于活动状态的所有目标。

who -r

runlevel

永久更改默认运行级别

使用服务管理器或运行以下命令:

ln -sf /usr/lib/systemd/system/ MY_TARGET.target /etc/systemd/system/default.target

使用服务管理器或更改以下行

id: X:initdefault:

in /etc/inittab

更改当前引导进程的默认运行级别

在引导提示处输入以下选项

systemd.unit= MY_TARGET.target

在引导提示处输入所需的运行级别编号。

显示目标/运行级别的依赖项

systemctl show -p "Requires" MY_TARGET.target

systemctl show -p "Wants" MY_TARGET.target

Requires 会列出硬性依赖项(必须解决的依赖项),而 Wants 会列出软性依赖项(情况允许时解决的依赖项)。

19.3.2 调试系统的启动

systemd 提供了分析系统启动过程的方法。您可以查看所有服务及其状态的列表(而不必分析 /var/log/)。systemd 还允许您扫描启动过程,以了解每项服务启动用了多长时间。

19.3.2.1 查看服务的启动情况

要查看系统引导后所启动服务的完整列表,请输入命令 systemctl。该命令会列出所有活动服务,如下所示(已精简)。要获得特定服务的详细信息,请使用 systemctl status MY_SERVICE

例 19.1︰ 列出活动服务
# systemctl
UNIT                        LOAD   ACTIVE SUB       JOB DESCRIPTION
[...]
iscsi.service               loaded active exited    Login and scanning of iSC+
kmod-static-nodes.service   loaded active exited    Create list of required s+
libvirtd.service            loaded active running   Virtualization daemon
nscd.service                loaded active running   Name Service Cache Daemon
chronyd.service             loaded active running   NTP Server Daemon
polkit.service              loaded active running   Authorization Manager
postfix.service             loaded active running   Postfix Mail Transport Ag+
rc-local.service            loaded active exited    /etc/init.d/boot.local Co+
rsyslog.service             loaded active running   System Logging Service
[...]
LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.

161 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.

如果只想列出无法启动的服务,请使用 --failed 选项:

例 19.2︰ 列出失败的服务
# systemctl --failed
UNIT                   LOAD   ACTIVE SUB    JOB DESCRIPTION
apache2.service        loaded failed failed     apache
NetworkManager.service loaded failed failed     Network Manager
plymouth-start.service loaded failed failed     Show Plymouth Boot Screen

[...]

19.3.2.2 调试启动时间

为了调试系统启动时间,systemd 提供了 systemd-analyze 命令。该命令会显示总启动时间及按启动时间排序的服务列表,还可以生成 SVG 图,以显示各服务相对于其他服务的启动时间。

列出系统启动时间
# systemd-analyze
Startup finished in 2666ms (kernel) + 21961ms (userspace) = 24628ms
列出服务启动时间
# systemd-analyze blame
    15.000s backup-rpmdb.service
    14.879s mandb.service
     7.646s backup-sysconfig.service
     4.940s postfix.service
     4.921s logrotate.service
     4.640s libvirtd.service
     4.519s display-manager.service
     3.921s btrfsmaintenance-refresh.service
     3.466s lvm2-monitor.service
     2.774s plymouth-quit-wait.service
     2.591s firewalld.service
     2.137s initrd-switch-root.service
     1.954s ModemManager.service
     1.528s rsyslog.service
     1.378s apparmor.service
    [...]
服务启动时间图
# systemd-analyze plot > jupiter.example.com-startup.svg
Image

19.3.2.3 查看完整的启动过程

上面的命令列出了已启动的服务及其启动时间。如需更详细的概述,请在引导提示符处指定以下参数,以指示 systemd 创建完整启动过程的详细日志。

systemd.log_level=debug systemd.log_target=kmsg

现在,systemd 会将日志消息写入内核环缓冲区。可以使用 dmesg 查看该缓冲区:

> dmesg -T | less

19.3.3 System V 兼容性

systemd 与 System V 兼容,因此,您仍可以使用现有的 System V init 脚本。但是,至少有一个已知问题会导致 System V init 脚本无法立即与 systemd 配合使用:通过 init 脚本中的 susudo 以其他用户身份启动服务会导致脚本失败,产生访问被拒绝错误。

使用 susudo 更改用户时,会启动 PAM 会话。完成 init 脚本后会终止此会话。因此,init 脚本启动的服务也会终止。要解决此问题,请执行以下步骤:

  1. 创建与 init 脚本同名、文件扩展名为 .service 的服务文件封装程序:

    [Unit]
    Description=DESCRIPTION
    After=network.target
    
    [Service]
    User=USER
    Type=forking1
    PIDFile=PATH TO PID FILE1
    ExecStart=PATH TO INIT SCRIPT start
    ExecStop=PATH TO INIT SCRIPT stop
    ExecStopPost=/usr/bin/rm -f PATH TO PID FILE1
    
    [Install]
    WantedBy=multi-user.target2

    UPPERCASE LETTERS 中写入的所有值替换为适当的值。

    1

    可选 — 仅当 init 脚本启动守护程序时才使用。

    2

    multi-user.target 在系统引导进入 graphical.target 时也会启动 init 脚本。如果只应在引导进入显示管理器时才将它启动,请使用 graphical.target

  2. 使用 systemctl start APPLICATION 启动 守护程序。

19.4 使用 YaST 管理服务

基本服务管理也可以通过 YaST 服务管理器模块实现。该模块支持启动、停止、启用和禁用服务。它还可让您显示服务的状态以及更改默认目标。要启动 YaST 模块,请选择 YaST ›  系统 ›  服务管理器

服务管理器
图 19.1︰ 服务管理器
更改默认系统目标

要更改系统引导到的目标,请从默认系统目标下拉框中选择某个目标。最常用的目标是图形界面(启动图形登录屏幕)和多用户(以命令行模式启动系统)。

启动或停止服务

从表中选择一个服务。状态列显示它当前是(活动)否(非活动)正在运行。通过选择启动停止可切换其状态。

为当前正在运行的会话启动或停止服务会更改其状态。要更改服务在整个重引导过程中的状态,您需要启用或禁用服务。

定义服务启动行为

服务可以在引导时自动启动,也可以手动启动。从表中选择一个服务。启动列显示它当前是手动启动还是引导时启动。通过选择启动模式可切换其状态。

要在当前会话中更改服务状态,您需要按照上述方式启动或停止服务。

查看状态消息

要查看某个服务的状态消息,请从列表中选择该服务,然后选择显示细节。其输出与 systemctl -l status MY_SERVICE 命令生成的输出完全相同。

19.5 自定义 systemd

以下各节介绍如何自定义 systemd 单元文件。

19.5.1 单元文件存储在哪里?

SUSE 附带的 systemd 单元文件存储在 /usr/lib/systemd/ 中。自定义的单元文件和插入式单元文件存储在 /etc/systemd/ 中。

警告
警告:防止您的自定义被覆盖

在自定义 systemd 时,请务必使用目录 /etc/systemd/,不要使用 /usr/lib/systemd/。否则,您的更改将在 systemd 下次更新时被覆盖。

19.5.2 使用插入式文件覆盖

插入式文件(或插入式)是仅覆盖单元文件特定设置的部分单元文件。插入式文件的优先级高于主配置文件。命令 systemctl edit SERVICE 会启动默认的文本编辑器,并在 /etc/systemd/system/NAME.service.d/ 中创建一个包含空白 override.conf 文件的目录。该命令还确保会向正在运行的 systemd 进程通知这些更改。

例如,要更改系统等待 MariaDB 启动的时间,请运行 sudo systemctl edit mariadb.service 并编辑打开的文件以仅包含修改后的行:

# Configures the time to wait for start-up/stop
TimeoutSec=300

调整 TimeoutSec 值并保存更改。要启用更改,请运行 sudo systemctl daemon-reload

有关详细信息,请参考可使用 man 1 systemctl 命令调用的手册页。

警告
警告:创建完整单元文件的副本

如果在 systemctl edit --full SERVICE 命令中使用 --full 选项,则会创建原始单元文件的副本,您可以在该副本中修改特定选项。我们不建议进行此类自定义,因为当 SUSE 更新单元文件时,其更改将被 /etc/systemd/system/ 目录中的自定义副本覆盖。此外,如果 SUSE 提供对发行套件插入式文件的更新,它们会覆盖使用 --full 创建的单元文件的副本。为了防止产生混淆并确保您的自定义设置始终有效,请使用插入式文件。

19.5.3 手动创建插入式文件

除了使用 systemctl edit 命令,您还可以手动创建插入式文件,以便更好地控制其优先级。通过此类插入式文件,您可以扩展单元和守护程序配置文件,而无需编辑或覆盖文件本身。它们存储在以下目录中:

/etc/systemd/*.conf.d//etc/systemd/system/*.service.d/

系统管理员添加和自定义的插入式文件。

/usr/lib/systemd/*.conf.d//usr/lib/systemd/system/*.service.d/

自定义软件包安装的插入式文件,用于覆盖上游设置。例如,SUSE 会提供 systemd-default-settings

提示
提示

有关单元搜索路径的完整列表,请参见手册页 man 5 systemd.unit

例如,要禁用由 systemd-journald 的默认设置强制执行的速率限制,请执行以下步骤:

  1. 创建名为 /etc/systemd/journald.conf.d 的目录。

    > sudo mkdir /etc/systemd/journald.conf.d
    注意
    注意

    目录名称必须跟在要使用插入式文件修补的服务名称之后。

  2. 在该目录中,创建一个包含要覆盖的选项的文件 /etc/systemd/journald.conf.d/60-rate-limit.conf,例如:

    > cat /etc/systemd/journald.conf.d/60-rate-limit.conf
    # Disable rate limiting
    RateLimitIntervalSec=0
  3. 保存更改并重新启动相应 systemd 守护程序的服务。

    > sudo systemctl restart systemd-journald
注意
注意:避免名称冲突

为避免插入式文件与 SUSE 自带的文件冲突,建议在所有插入式文件名前加上一个两位数和一个短划线,例如,80-override.conf

以下范围为预留值范围:

  • 0-19,为 systemd 上游预留。

  • 20-29,为 SUSE 自带的 systemd 预留。

  • 30-39,为 SUSE 软件包(systemd 除外)预留。

  • 40-49,为第三方软件包预留。

  • 50,为使用 systemctl set-property 创建的插入式单元文件预留。

请使用大于此范围的两位数,以确保 SUSE 自带的任何插入式文件都不会覆盖您自己的插入式文件。

提示
提示

您可以使用 systemctl cat $UNIT 来列出并校验单元配置中将哪些文件纳入考量。

提示
提示

由于 systemd 组件的配置可能分散在文件系统的不同位置,因此可能难以全面掌握情况。要检查 systemd 组件的配置,请使用以下命令:

  • systemctl cat UNIT_PATTERN 会列显与一个或多个 systemd 单元相关的配置文件,例如:

    > systemctl cat atd.service
  • systemd-analyze cat-config DAEMON_NAME_OR_PATH 会复制配置文件的内容和 systemd 守护程序的插入式文件,例如:

    > systemd-analyze cat-config systemd/journald.conf

19.5.4 xinetd 服务转换为 systemd

SUSE Linux Enterprise Server 15 版本开始,去除了 xinetd 基础架构。本节概述如何将现有的自定义 xinetd 服务文件转换为 systemd 套接字。

对于每个 xinetd 服务文件,您至少要有两个 systemd 单元文件:套接字文件 (*.socket) 和关联的服务文件 (*.service)。套接字文件告诉 systemd 要创建哪个套接字,服务文件告诉 systemd 要启动哪个可执行文件。

以下面的 xinetd 服务文件为例:

# cat /etc/xinetd.d/example
service example
{
  socket_type = stream
  protocol = tcp
  port = 10085
  wait = no
  user = user
  group = users
  groups = yes
  server = /usr/libexec/example/exampled
  server_args = -auth=bsdtcp exampledump
  disable = no
}

要将它转换为 systemd,需要下面两个配套的文件:

# cat /usr/lib/systemd/system/example.socket
[Socket]
ListenStream=0.0.0.0:10085
Accept=false

[Install]
WantedBy=sockets.target
# cat /usr/lib/systemd/system/example.service
[Unit]
Description=example

[Service]
ExecStart=/usr/libexec/example/exampled -auth=bsdtcp exampledump
User=user
Group=users
StandardInput=socket

有关 systemd socketservice文件选项的完整列表,请参见 systemd.socket 和 systemd.service 手册页(man 5 systemd.socketman 5 systemd.service)。

19.5.5 创建自定义目标

System V init SUSE 系统上未使用运行级别 4,以便允许管理员创建自己的运行级别配置。systemd 允许您创建任意数目的自定义目标。建议您从采用 graphical.target 等现有目标开始。

  1. 将配置文件 /usr/lib/systemd/system/graphical.target 复制到 /etc/systemd/system/MY_TARGET.target,并根据需要调整该文件。

  2. 上一步中复制的配置文件已涵盖目标的必要(硬性)依赖项。如果还要涵盖需要的(软性)依赖项,请创建目录 /etc/systemd/system/MY_TARGET.target.wants

  3. 对每个需要的服务,创建从 /usr/lib/systemd/system 指向 /etc/systemd/system/MY_TARGET.target.wants 的符号链接。

  4. 设置好目标后,重新加载 systemd 配置以使新目标可用:

    > sudo systemctl daemon-reload

19.6 高级用法

以下几节介绍了适合系统管理员的高级主题。有关更高级的 systemd 文档,请参考 Lennart Pöttering 撰写的适用于管理员的 systemd 相关系列,网址为 https://0pointer.de/blog/projects/

19.6.1 清理临时目录

systemd 支持定期清理临时目录。将会自动迁移并激活前一系统版本中的配置。tmpfiles.d(负责管理临时文件)会从 /etc/tmpfiles.d/*.conf/run/tmpfiles.d/*.conf/usr/lib/tmpfiles.d/*.conf 文件中读取其配置。/etc/tmpfiles.d/*.conf 中的配置会覆盖其他两个目录中的相关配置(/usr/lib/tmpfiles.d/*.conf 是软件包用于存储其配置文件的位置)。

配置格式为每个路径一行,该行包含操作与路径、(可选)模式、所有权、期限和参数字段,具体取决于操作。以下示例将取消链接 X11 锁文件:

Type Path               Mode UID  GID  Age Argument
r    /tmp/.X[0-9]*-lock

要获取 tmpfile 计时器的状态:

> sudo systemctl status systemd-tmpfiles-clean.timer
systemd-tmpfiles-clean.timer - Daily Cleanup of Temporary Directories
 Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.timer; static)
 Active: active (waiting) since Tue 2018-04-09 15:30:36 CEST; 1 weeks 6 days ago
   Docs: man:tmpfiles.d(5)
         man:systemd-tmpfiles(8)

Apr 09 15:30:36 jupiter systemd[1]: Starting Daily Cleanup of Temporary Directories.
Apr 09 15:30:36 jupiter systemd[1]: Started Daily Cleanup of Temporary Directories.

有关临时文件处理的详细信息,请参见 man 5 tmpfiles.d

19.6.2 系统日志

第 19.6.9 节 “调试服务” 介绍如何查看给定服务的日志消息。但可显示的日志消息并不仅限于服务日志。您还可以访问和查询 systemd 写入的完整日志消息,即所谓的日记。使用 journalctl 命令可显示完整的日志消息,从最早的项开始。有关诸如应用过滤器或更改输出格式的选项,请参考 man 1 journalctl

19.6.3 快照

您可以使用 isolate 子命令将 systemd 的当前状态保存到指定快照中,并在日后还原到该状态。此功能在测试服务或自定义目标时非常有用,因为它可让您随时回到定义的状态。快照仅在当前会话中可用,重引导时将自动删除。快照名称必须以 .snapshot 结尾。

创建快照
> sudo systemctl snapshot MY_SNAPSHOT.snapshot
删除快照
> sudo systemctl delete MY_SNAPSHOT.snapshot
查看快照
> sudo systemctl show MY_SNAPSHOT.snapshot
激活快照
> sudo systemctl isolate MY_SNAPSHOT.snapshot

19.6.4 加载内核模块

systemd 结合 /etc/modules-load.d 中的配置文件,可以在系统引导时自动加载内核模块。该文件应该命名为 MODULE.conf 并包含以下内容:

# load module MODULE at boot time
MODULE

如果某个软件包安装了用于加载内核模块的配置文件,该文件将安装到 /usr/lib/modules-load.d。如果存在两个同名的配置文件,将以 /etc/modules-load.d 中的配置文件为准。

有关更多信息,请参见 modules-load.d(5) 手册页。

19.6.5 加载服务之前执行操作

使用 System V init 时,需在 /etc/init.d/before.local 中指定需要在加载服务之前执行的操作。systemd 不再支持此过程。如果您需要在启动服务之前执行操作,请执行以下步骤:

加载内核模块

/etc/modules-load.d 目录中创建一个插入式文件(有关语法,请参见 man modules-load.d

创建文件或目录、清理目录、更改所有权

/etc/tmpfiles.d 中创建一个插入式文件(有关语法,请参见 man tmpfiles.d

其他任务

基于下面的模板创建一个系统服务文件,例如 /etc/systemd/system/before.service

[Unit]
Before=NAME OF THE SERVICE YOU WANT THIS SERVICE TO BE STARTED BEFORE
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=YOUR_COMMAND
# beware, executable is run directly, not through a shell, check the man pages
# systemd.service and systemd.unit for full syntax
[Install]
# target in which to start the service
WantedBy=multi-user.target
#WantedBy=graphical.target

创建服务文件后,应运行以下命令(以 root 身份):

> sudo systemctl daemon-reload
> sudo systemctl enable before

每次修改服务文件时,都需要运行:

> sudo systemctl daemon-reload

19.6.6 内核控制组 (cgroup)

在传统 System V init 系统上,并不总是能够将某个进程与生成它的服务匹配。一些服务(例如 Apache)会生成大量第三方进程(例如,CGI 或 Java 进程),这些进程本身又会生成更多进程。这使得明确指派变得非常困难,甚至无法做到。另外,服务可能不会正常结束,残留某些子进程保持运行状态。

systemd 通过将每个服务放入它自己的 cgroup 中,解决了这个问题。cgroups 是一项内核功能,允许将进程及其所有子进程聚合到分层组织的组中。systemd 根据每个 cgroup 的服务名称命名各 cgroup。因为非特权进程不允许离开它的 cgroup,这提供了一种行之有效的方式,可通过服务名称来标记该服务生成的所有进程。

要列出属于某个服务的所有进程,请使用命令 systemd-cgls,例如:

例 19.3︰ 列出属于某个服务的所有进程
# systemd-cgls --no-pager
├─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 20
├─user.slice
│ └─user-1000.slice
│   ├─session-102.scope
│   │ ├─12426 gdm-session-worker [pam/gdm-password]
│   │ ├─15831 gdm-session-worker [pam/gdm-password]
│   │ ├─15839 gdm-session-worker [pam/gdm-password]
│   │ ├─15858 /usr/lib/gnome-terminal-server

[...]

└─system.slice
  ├─systemd-hostnamed.service
  │ └─17616 /usr/lib/systemd/systemd-hostnamed
  ├─cron.service
  │ └─1689 /usr/sbin/cron -n
  ├─postfix.service
  │ ├─ 1676 /usr/lib/postfix/master -w
  │ ├─ 1679 qmgr -l -t fifo -u
  │ └─15590 pickup -l -t fifo -u
  ├─sshd.service
  │ └─1436 /usr/sbin/sshd -D

[...]

有关 cgroups 的更多信息,请参见第 10 章 “内核控制组

19.6.7 终止服务(发送信号)

第 19.6.6 节 “内核控制组 (cgroup)”中所述,在 System V init 系统中,并不总是能够将某个进程指派给其父服务。这导致难以停止服务及其子进程。尚未终止的子进程会一直保持为僵停状态。

systemd 的概念是将每个服务限定在某个 cgroup 中,从而可以识别一个服务的所有子进程,这样便可将信号发送给这些进程中的每一个进程。您可使用 systemctl kill 向服务发送信号。有关可用信号的列表,请参考 man 7 signals

向服务发送 SIGTERM

SIGTERM 是发送的默认信号。

> sudo systemctl kill MY_SERVICE
向服务发送 SIGNAL

可使用 -s 选项指定应该发送的信号。

> sudo systemctl kill -s SIGNAL MY_SERVICE
选择进程

默认情况下,kill 命令会向指定 cgroup 的 all 进程发送信号。您可以将发送目标限制为 controlmain 进程。例如,要通过发送 SIGHUP 强制服务重新加载其配置时,便可使用后一个选项:

> sudo systemctl kill -s SIGHUP --kill-who=main MY_SERVICE

19.6.8 有关 D-Bus 服务的重要说明

D-Bus 服务是 systemd 客户端与作为 pid 1 运行的 systemd 管理器之间进行通讯的消息总线。虽然 dbus 是个独立的守护程序,但它也是 init 基础架构的组成部分。

在正在运行的系统中停止或重启动 dbus 的效果类似于尝试停止或重启动 PID 1。此操作会中断 systemd 客户端与服务器间的通讯,并使大部分 systemd 功能不可用。

因此,不建议也不支持终止或重启动 dbus

更新 dbus 或与 dbus 有关的软件包需要重引导系统。如果不确定是否需要重引导,请运行 sudo zypper ps -s。如果 dbus 显示在所列服务之中,则表明您需要重引导系统。

请记住,即使自动更新配置为跳过需要重引导的软件包,dbus 仍会更新。

19.6.9 调试服务

默认情况下,systemd 的输出不会太详细。如果服务成功启动,则不会产生任何输出。如果服务启动失败,则会显示简短的错误消息。不过,systemctl status 提供了调试服务的启动和操作的方法。

systemd 附带了自己的日志记录机制(日记)来记录系统消息。这可让您一并显示服务消息与状态消息。status 命令的工作方式与 tail 相似,也可以采用不同的格式显示日志消息,是一个功能强大的调试工具。

显示服务启动失败消息

每当服务启动失败时,使用 systemctl status MY_SERVICE 可获得详细的错误消息:

# systemctl start apache2
Job failed. See system journal and 'systemctl status' for details.
# systemctl status apache2
   Loaded: loaded (/usr/lib/systemd/system/apache2.service; disabled)
   Active: failed (Result: exit-code) since Mon, 04 Apr 2018 16:52:26 +0200; 29s ago
   Process: 3088 ExecStart=/usr/sbin/start_apache2 -D SYSTEMD -k start (code=exited, status=1/FAILURE)
   CGroup: name=systemd:/system/apache2.service

Apr 04 16:52:26 g144 start_apache2[3088]: httpd2-prefork: Syntax error on line
205 of /etc/apache2/httpd.conf: Syntax error on li...alHost>
显示最后 N 条服务消息

status 子命令的默认行为是显示服务发出的最后 10 条消息。如果要更改要显示的消息数目,请使用 --lines=N 参数:

> sudo systemctl status chronyd
> sudo systemctl --lines=20 status chronyd
以附加模式显示服务消息

要显示服务消息的实时流,请使用 --follow 选项,效果与 tail -f 相似:

> sudo systemctl --follow status chronyd
消息输出格式

--output=MODE 参数可让您更改服务消息的输出格式。最重要的可用模式包括:

short

默认格式。显示日志消息,以及用户能看懂的时戳。

verbose

所有字段的完整输出。

cat

精简输出,不含时戳。

19.7 systemd 计时器单元

与 cron 类似,systemd 计时器单元提供了一种在 Linux 上安排作业的机制。虽然 systemd 计时器单元的用途与 cron 相同,但其具备几项优点。

  • 使用计时器单元安排的作业可以依赖其他 systemd 服务。

  • 计时器单元被视为常规 systemd 服务,因此可以使用 systemctl 进行管理。

  • 计时器可以是实时计时器,也可以是单调计时器。

  • 时间单元会记录到 systemd 日记中,这样用户便可更轻松地对它们进行监控和查错。

systemd 计时器单元通过 .timer 文件扩展名标识。

19.7.1 systemd 计时器类型

计时器单元可以使用单调和实时计时器。

  • 与 cron 作业类似,实时计时器是根据日历事件触发的。实时计时器使用选项 OnCalendar 进行定义。

  • 单调计时器将从某个特定起点开始经过指定时间后触发。后者可以是系统引导或系统单元激活事件。有几个选项可用于定义单调计时器,包括 OnBootSecOnUnitActiveSecOnTypeSec。单调计时器不是永久的,每次重引导后都会被重置。

19.7.2 systemd 计时器和服务单元

每个计时器单元都必须有一个其所控制的对应 systemd 单元文件。也就是说,将由 .timer 文件激活并管理对应的 .service 文件。与计时器搭配使用时,.service 文件不需要 [Install] 部分,因为该服务将由计时器管理。

19.7.3 实际示例

为了理解 systemd 计时器单元的基本知识,我们设置了一个触发 foo.sh 外壳脚本的计时器。

第一步是创建用于控制外壳脚本的 systemd 服务单元。为此,请打开一个新的文本文件进行编辑,并添加以下服务单元定义:

[Unit]
Description="Foo shell script"

[Service]
ExecStart=/usr/local/bin/foo.sh

将文件以名称 foo.service 保存到目录 /etc/systemd/system/ 中。

接下来,打开一个新的文本文件进行编辑,并添加以下计时器定义:

[Unit]
Description="Run foo shell script"

[Timer]
OnBootSec=5min
OnUnitActiveSec=24h
Unit=foo.service

[Install]
WantedBy=multi-user.target

上述示例中的 [Timer] 部分指定了要触发的服务 (foo.service) 以及触发时间。在本例中,选项 OnBootSec 指定了一个在系统引导五分钟后触发服务的单调计时器,而选项 OnUnitActiveSec 会在服务被激活后 24 小时触发服务(即计时器会每天触发一次服务)。最后,选项 WantedBy 指定应在系统达到多用户目标时启动计时器。

您也可以不指定单调计时器,而是使用选项 OnCalendar 指定实时计时器。下面的实时计时器定义从星期一 12:00 开始,每周触发一次相关服务单元。

[Timer]
OnCalendar=weekly
Persistent=true

选项 Persistent=true 指示如果计时器错过了上一次启动时间(例如,由于系统关闭),则在计时器激活后立即触发服务。

使用选项 OnCalendar 并采用以下格式还可以定义触发服务的具体日期和时间:DayOfWeek Year-Month-Day Hour:Minute:Second。下面的示例会在每天早上 5 点触发服务:

OnCalendar=*-*-* 5:00:00

您可以使用星号指定任何值,使用逗号列出可能的值。使用由 .. 分隔的两个值可表示一个连续的范围。下面的示例会在每个月的星期五下午 6 点触发服务:

OnCalendar=Fri *-*-1..7 18:00:00

要在不同的时间触发服务,您可以指定多个 OnCalendar 项:

OnCalendar=Mon..Fri 10:00
OnCalendar=Sat,Sun 22:00

在上面的示例中,会在工作日的上午 10 点和周末的晚上 10 点触发服务。

编辑好计时器单元文件后,使用名称 foo.timer 将其保存到 /etc/systemd/system/ 目录中。要检查所创建的单元文件是否正确,请运行以下命令:

> sudo  systemd-analyze verify /etc/systemd/system/foo.*

如果命令未返回任何输出,则表示文件成功通过了校验。

要启动计时器,请使用命令 sudo systemctl start foo.timer。要在引导时启用计时器,请运行命令 sudo systemctl enable foo.timer

19.7.4 管理 systemd 计时器

由于计时器被视为常规 systemd 单元,因此您可以使用 systemctl 来管理它们。您可以使用 systemctl start 启动计时器,使用 systemctl enable 启用计时器,依此类推。此外,您还可以使用命令 systemctl list-timers 列出所有活动计时器。要列出所有计时器(包括非活动计时器),请运行命令 systemctl list-timers --all

19.8 更多信息

有关 systemd 的详细信息,请参考以下联机资源:

主页

https://systemd.io/

面向管理员的 systemd

Lennart Pöttering 是 systemd 的作者之一,他撰写了一系列博客文章(写本章时已有 13 篇)。它们可在 https://0pointer.de/blog/projects/ 找到。