systemd
摘自项目主页:
- systemd 是一个 Linux 系统基础组件的集合,提供了一个系统和服务管理器,运行为 PID 1 并负责启动其它程序。功能包括:支持并行化任务;同时采用 socket 式与 D-Bus 总线式启用服务;按需启动守护进程(daemon);利用 Linux 的 cgroups 监视进程;支持快照和系统恢复;维护挂载点和自动挂载点;各服务间基于依赖关系进行精密控制。systemd 支持 SysV 和 LSB 初始脚本,可以替代 sysvinit。除此之外,功能还包括日志进程、控制基础系统配置,维护登录用户列表以及系统账户、运行时目录和设置,可以运行容器和虚拟机,可以简单的管理网络配置、网络时间同步、日志转发和名称解析等。
在历史上,systemd中的“服务”(service)被称作守护进程(daemon),它们在后台运行(即没有UI、不与终端交互),等待特定事件的发生并提供服务。例如,Web服务器会等待一个请求以提供相应的页面,SSH服务器会等待登录请求。除了这种提供完整功能的,还有一些守护进程的工作是隐藏在幕后的,如负责向日志文件写入消息的syslog
、metalog
,确保系统时间准确的ntpd。更多信息详见daemon(7)。
systemctl 基本用法[编辑 | 编辑源代码]
监视和控制 systemd 的主要命令是 systemctl。其用途包括查看系统状态以及管理系统和服务。详见 systemctl(1)。
- 在 systemctl 参数中添加
-H 用户名@主机名
可以远程控制其他机器。该功能使用 SSH 连接到远程的 systemd 实例。 - Plasma 用户可以安装 systemctl 图形前端 systemdgenie包。安装后可以在系统管理下找到。
使用单元[编辑 | 编辑源代码]
单元(unit)通常包括但不限于:服务(.service)、挂载点(.mount)、设备(.device)和套接字(.socket)。
使用 systemctl 时,通常需要使用单元文件的全名,包括扩展名(例如 sshd.socket
)。不过在以下 systemctl 命令中可以使用简写:
- 如果无扩展名,systemctl 会假定扩展名为 .service。例如,
netctl
和netctl.service
是等价的。 - 挂载点会自动转化为相应的 .mount 单元。例如,
/home
等价于home.mount
。 - 与挂载点类似,设备会自动转化为相应的 .device 单元,因此
/dev/sda2
等价于dev-sda2.device
。
详见 systemd.unit(5)。
@
符号(例如:name@string.service
),这意味着它是模板单元 name@.service
的一个 实例,模板单元的实际文件名中不包括 string
部分(如 name@.service
)。string
被称作实例标识符,在 systemctl 调用模板单元时,会将其当作一个参数传给模板单元,模板单元会使用这个传入的参数代替模板中的 %i
指示符。在启动单元时,尝试从模板单元实例化之前,systemd 会先检查 name@string.suffix
文件是否存在。如果存在,就直接使用这个文件,而不是模板实例化(不过,这种“碰撞”非常少见)。大多数情况下,包含 @
标记都意味着这个文件是模板。如果一个模板单元被调用时没有指定实例标识符,该调用通常会失败,除非是在某些特殊的 systemctl命令(如cat
)中使用。因为调用systemctl时默认了 --system
参数,下列命令默认对系统单元进行操作。若要对(调用用户的)用户单元进行操作,则需在非root身份下执行systemctl --user。参看systemd/用户#基础设置以为所有用户启用或禁用单元。
- 下面的大部分命令都可以跟多个单元名,详细信息参见 systemctl(1)。
--now
选项可与enable
、disable
和mask
同时使用,可使这些动作立即生效,而非重启后生效。- 一个软件包可能会为不同的功能提供多个不同的单元。如果你刚安装了软件包,可以通过
pacman -Qql package | grep -Fe .service -e .socket
命令检查这个软件包提供了哪些单元。
动作 | 命令 | 注释 |
---|---|---|
分析系统状态 | ||
显示系统状态 | systemctl status |
|
列出正在运行的单元 | systemctl 或systemctl list-units |
|
列出失败的单元 | systemctl --failed |
|
列出已安装的单元1 | systemctl list-unit-files |
|
显示特定PID对应进程的状态 | systemctl status pid |
cgroup slice,内存占用,父进程 |
检查单元状态 | ||
显示单元的手册页 | systemctl help 单元 |
如果单元支持 |
显示单元的状态 | systemctl status 单元 |
包括其是否在运行 |
检查单元是否配置为自动启动(enabled) | systemctl is-enabled 单元 |
|
启动(start)、重新启动和重新加载单元 | ||
立即启动单元 | 以root身份执行systemctl start 单元 |
|
立即停止单元 | 以root身份执行systemctl stop 单元 |
|
重新启动单元 | 以root身份执行systemctl restart 单元 |
|
重新加载单元及其配置 | 以root身份执行systemctl reload 单元 |
|
重新加载 systemd 配置2 | 以root身份执行systemctl daemon-reload |
扫描单元的变动 |
启用(enable)单元 | ||
启用单元:开机时自动启动该单元 | 以root身份执行systemctl enable 单元 |
|
启用单元,并立即启动它 | 以root身份执行systemctl enable --now 单元 |
|
取消开机自动启动单元 | 以root身份执行systemctl disable 单元 |
|
重新启用单元3 | 以root身份执行systemctl reenable 单元 |
先取消启用,再启用 |
屏蔽单元 | ||
屏蔽单元,使其无法启动4 | 以root身份执行systemctl mask 单元 |
|
取消屏蔽单元 | 以root身份执行systemctl unmask 单元 |
- systemd.unit(5) § UNIT FILE LOAD PATH 中描述了查找单元文件的路径。
- 这不会要求已改变的单元重新加载自己的配置(见重新加载动作)。
- 可在单元的
[Install]
节发生变更(在上一次启用后)时使用。 - 既不能手动启动,也无法作为依赖启动。因此屏蔽单元是危险的。 查看已屏蔽的单元:
# systemctl list-unit-files --state=masked
电源管理[编辑 | 编辑源代码]
安装 polkit 后才能以普通用户身份使用电源管理。如果你正登录在一个本地的 systemd-logind 用户会话,且当前没有其它活动的会话,那么以下命令无需 root 权限即可执行。否则(例如,当前有另一个用户登录在某个 tty),systemd 将会自动请求输入 root 密码。
动作 | 命令 |
---|---|
重启 | systemctl reboot
|
关机 | systemctl poweroff
|
待机 | systemctl suspend
|
休眠(将内存写入硬盘) | systemctl hibernate
|
进入混合休眠模式(同时休眠到硬盘并待机) | systemctl hybrid-sleep
|
先待机,在一个特定时间唤醒后进入休眠模式 | systemctl suspend-then-hibernate
|
使用#软重启来仅重启用户空间 | systemctl soft-reboot
|
软重启[编辑 | 编辑源代码]
软重启是一种不涉及内核,仅重启用户空间的重启操作。这个操作由 systemd-soft-reboot.service(8) 实现,并且可以通过 systemctl soft-reboot
来进行一次软重启。正如 kexec 一样,这个操作跳过了重新初始化固件,也不会进行内核初始化和初始化 RAM 文件系统。同时,未锁定的 dm-crypt 设备也将保持解锁状态。
当 /run/nextroot
包含了一个合法的 rootfs 结构(比如说挂载了另一个发行版的根分区或者另一个快照),软重启操作将会切换系统根分区到其中。切换之后并不会丢失一些由内核管理的状态,比如说网络连接。
/run/nextroot/
不一定是一个挂载点或者存在于物理设备上。例如,它可以在 /run/
tmpfs 中。systemd 将会在软重启时自动将/run/nextroot/
转化为一个挂载点。 systemctl soft-reboot
。编写单元文件[编辑 | 编辑源代码]
systemd
单元文件的语法来源于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要求单元B在A启动之前运行。在此情况下,可向单元A配置文件中的 [Unit]
段添加 Requires=B
和 After=B
即可。若此依赖关系是可选的,可添加 Wants=B
和 After=B
。请注意 Wants=
和 Requires=
并不暗含 After=
,即如果 After=
选项没有指定,这两个单元将被并行启动。
依赖关系通常被用在服务(service)而不是目标(target)上。例如,network.target
一般会被某个配置网络接口的服务引入作为依赖。所以,若要让自定义单元在系统到达network.target
后再启动, 将自定义的单元排在该类配置网络接口的服务之后即可,因为该类服务启动时 network.target
一定已经启动。
服务(service)类型[编辑 | 编辑源代码]
编写自定义的服务(service)文件时,可以选择几种不同的服务启动方式。启动方式可通过配置文件 [Service]
段中的 Type=
参数进行设置。
Type=simple
:(默认值) systemd认为该服务将立即启动且服务进程不会fork。Do not use this type if other services need to be ordered on this service, unless it is socket activated.Type=forking
:systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于典型的守护进程,除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定PIDFile=
,以便systemd能够跟踪服务的主进程。Type=oneshot
:这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置RemainAfterExit=yes
使得systemd在服务进程退出之后仍然认为服务处于启用状态,这对于改变系统状态(如挂载分区)的单元尤其适用。对于 simple 和 oneshot 类型的区别,请参阅 [1]。Type=notify
:与Type=simple
相同,但约定服务会在就绪后向systemd发送一个信号以通知systemd。这一通知的参考实现由libsystemd-daemon.so
提供。Type=dbus
:若以此方式启动,当指定的BusName
出现在DBus系统总线上时,systemd认为服务就绪。Type=idle
:systemd
会等待所有活动任务都完成后再执行服务进程。其他行为与Type=simple
类似。
type
的更多解释可以参考 systemd.service(5) § OPTIONS。
修改现存单元文件[编辑 | 编辑源代码]
为了避免和 pacman 冲突,不应该直接编辑软件包提供的文件。有两种方法可以不改动原始文件就做到修改单元文件:创建一个优先级更高的本地单元文件以覆盖原有的单元文件或创建一个片段(drop-in snippets),应用到原始单元文件之上。两种方法都需要在修改后重新加载单元:用 systemctl edit
编辑单元(会自动重载单元)或通过下面命令重新加载单元:
# systemctl daemon-reload
systemd-delta
命令用来查看哪些单元文件被覆盖、扩增,哪些被修改。- 使用
systemctl cat unit
可以查看单元的内容和所有相关的片段。
替换单元文件[编辑 | 编辑源代码]
要替换 /usr/lib/systemd/system/unit
, 创建文件 /etc/systemd/system/unit
并重新启用单元以更新符号链接:
# systemctl reenable unit
或者运行:
# systemctl edit --full unit
这将会在默认编辑器中打开 /etc/systemd/system/unit
。如果文件不存在,可以将软件包安装的版本复制到这里,在编辑完成之后,会自动加载新版本。
附加配置片段[编辑 | 编辑源代码]
要为单元文件/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
。
- 附加配置片段中的键(配置项)仍必须置于相应的节(section)下。
- 并不是所有参数都会被子配置文件覆盖。例如要修改
Conflicts=
就必须替换原始文件。
重置到软件包的版本[编辑 | 编辑源代码]
要回退使用systemctl edit
对单元进行的所有变更,执行:
# systemctl revert unit
示例[编辑 | 编辑源代码]
如果想添加一个额外的依赖,创建如下文件即可:
/etc/systemd/system/<unit>.d/customdependency.conf
[Unit] Requires=<新依赖> After=<新依赖>
要修改一个单元的 ExecStart
命令,创建下面文件:
/etc/systemd/system/unit.d/customexec.conf
[Service] ExecStart= ExecStart=<新命令>
修改 ExecStart
前必须将其置空,参见 [2] 。所有可能多次赋值的变量都需要这个操作,例如定时器的 OnCalendar
。
下面是自动重启服务的一个例子:
/etc/systemd/system/unit.d/restart.conf
[Service] Restart=always RestartSec=30
目标(target)[编辑 | 编辑源代码]
systemd使用目标来通过依赖关系将多个单元组织起来。目标还是系统的标准化同步点(确保系统处于特定状态)。目标与runlevels的设计目的相似,但两者也有些许不同。每一个目标都以名称而不是数字标识,用以达成特定的目的。多个目标可以同时被激活。有的目标继承另一个目标的所有服务,同时向其中增添一些服务。有些systemd目标模仿了常见的SystemVinit runlevels,所以仍可用熟悉的telinit RUNLEVEL
命令切换目标。
获取当前目标[编辑 | 编辑源代码]
使用systemd的命令而非runlevel
:
$ systemctl list-units --type=target
创建自定义目标[编辑 | 编辑源代码]
在sysvinit中有明确定义的运行级别(如:0、1、3、5、6)与 systemd 中特定的目标存在一一对应的关系。然而,对于用户自定义运行级别(2、4)却没有。如需要同样功能,建议你以原有运行级别所对应的 systemd 目标为基础,新建一个/etc/systemd/system/<目标名>.target
(可参考/usr/lib/systemd/system/graphical.target
), 然后创建目录/etc/systemd/system/<目标名>.wants
,并向其中加入需启用的服务链接(指向/usr/lib/systemd/system/
中的对应文件)。
“SysV运行级别”与“systemd目标”对照表[编辑 | 编辑源代码]
SysV 运行级别 | Systemd 目标 | 注释 |
---|---|---|
0 | runlevel0.target, poweroff.target | 关闭系统(halt) |
1, s, single | runlevel1.target, rescue.target | 单用户模式 |
2, 4 | runlevel2.target, runlevel4.target, multi-user.target | 用户自定义运行级别,默认与级别3相同。 |
3 | runlevel3.target, multi-user.target | 多用户,无图形界面。用户可以通过终端或网络登录。 |
5 | runlevel5.target, graphical.target | 多用户,图形界面。继承级别3的服务,并启动图形界面服务。 |
6 | runlevel6.target, reboot.target | 重启 |
emergency | emergency.target | 急救模式(Emergency shell) |
切换当前运行目标[编辑 | 编辑源代码]
systemd中,目标通过“目标单元”访问。通过如下命令切换:
# systemctl isolate graphical.target
该命令仅更改当前运行目标,对下次启动无影响。这等价于sysvinit中的telinit 3
或telinit 5
命令。
更改开机默认启动目标[编辑 | 编辑源代码]
开机启动的目标是default.target
,默认符号链接到graphical.target
(大致相当于原来的运行级别5)。
用 systemctl 检查当前的默认启动目标:
# systemctl get-default
用systemctl修改default.target
符号链接以变更开机默认启动目标:
# 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.
另一方法是向bootloader添加内核参数:
systemd.unit=multi-user.target
(大致相当于运行级别3)systemd.unit=rescue.target
(大致相当于运行级别1)
默认目标顺序[编辑 | 编辑源代码]
Systemd 根据下面顺序选择 default.target
:
- 上面的内核参数
/etc/systemd/system/default.target
软链接/usr/lib/systemd/system/default.target
软链接
systemd组件[编辑 | 编辑源代码]
systemd的部分组件如下:
- kernel-install——一个用以自动将内核及对应的initramfs镜像移动到boot分区的脚本;
- systemd-boot——简单的UEFI启动管理器;
- systemd-creds——安全地存储并获取systemd单元使用的credentials;
- systemd-cryptenroll(1)——Enroll PKCS#11, FIDO2, TPM2 token/devices to LUKS2 encrypted volumes;
- systemd-firstboot——负责在系统第一次启动前的设置初始化;
- systemd-homed——portable human-user accounts;
- systemd-logind(8)——会话管理;
- systemd-networkd——网络配置管理;
- systemd-nspawn——轻量namespace容器;
- systemd-resolved——网络域名解析;
- systemd-run(1) / run0(1)——以交互式的方式临时的提升或获得不同的权限。;
- systemd-stub(7)——用于创建统一内核镜像的UEFI boot stub;
- systemd-sysusers(8)——在安装软件包或系统启动时创建系统用户和组、将用户加入组;
- systemd-timesyncd——通过网络同步系统时间;
- systemd/Journal——系统日志;
- systemd/Timers——用于控制.service文件或事件的间隔或实时计时器,可替代cron。
systemd.mount - 挂载[编辑 | 编辑源代码]
systemd负责挂载/etc/fstab
中指定的分区和文件系统。systemd-fstab-generator(8)将/etc/fstab
中的所有条目翻译为systemd单元。该过程将在系统启动或系统管理器的配置被重新加载时执行。
systemd扩展了通常fstab的用法并提供了更多挂载选项。这些挂载选项可影响挂载点单元的依赖关系。例如,它们可以确保某一挂载操作只会在连接网络或另一分区挂载后进行。systemd的特定挂载选项(大多以x-systemd.
开头)的完整列表见systemd.mount(5) § FSTAB。
automounting是这些挂载选项的一个例子。它意味着资源只会在请求时挂载,而非在系统启动时自动挂载,详见fstab#通过 systemd 自动挂载。
GPT分区自动挂载[编辑 | 编辑源代码]
在UEFI启动的系统上,当特定条件满足时,systemd-gpt-auto-generator(8)会根据Discoverable Partitions Specification自动挂载GPT分区。因此,可在fstab中省略自动挂载的分区。此外,若根分区已被自动挂载,可省去内核命令行中的root=
。
使用GPT分区自动挂载的先决条件有:
- 引导加载程序必须设置LoaderDevicePartUUIDEFI变量,以便识别使用的EFI系统分区。systemd-boot,systemd-stub(7),GRUB (使用grub-mkconfig生成
grub.cfg
),rEFInd(默认未启用)支持该特性。若使用自定义的grub.cfg
,则需要加载bli模块。 可通过执行bootctl
并检查Boot loader sets ESP information
的状态进行确认,或在用统一内核镜像启动时检查Stub sets ESP information
的状态。 - 根分区与EFI系统分区(ESP)必须在同一块物理硬盘上。其它自动挂载的分区必须与根分区在同一块物理硬盘上。这基本上意味着所有自动挂载分区必须和ESP分区在同一块物理硬盘上。
- 若需要
/efi
挂载点,则必须手动创建它。否则systemd-gpt-auto-generator
将使用/boot
作为挂载点。
/efi
时需小心行事。在下次启动时,/efi
将作为默认挂载点,这可能导致/boot
目录为空,使系统陷入不稳定状态。若出现这种情况,将需要执行重新安装内核、处理器微码并重新生成initramfs等操作。/var[编辑 | 编辑源代码]
要自动挂载/var
分区,分区的PARTUUID必须与以machine ID为密钥,分区类型UUID(4d21b016-b534-45c2-a9fb-5c16e091fd2d
)为消息的SHA256 HMAC哈希计算结果相同。可通过如下命令得到符合要求的PARTUUID:
$ systemd-id128 -u --app-specific=4d21b016-b534-45c2-a9fb-5c16e091fd2d machine-id
/etc/machine-id
读取machine ID,因此必须在系统安装后才能得到符合要求的PARTUUID。systemd-sysvcompat[编辑 | 编辑源代码]
systemd-sysvcompat包(由base包需要)的主要工作是提供传统的Linux init可执行文件。在由systemd控制的系统上,init
只是一个到systemd
可执行文件的符号链接。
此外,该包还提供了SysVinit用户可能需要的功能的4个快捷方式:halt(8),poweroff(8),reboot(8)和shutdown(8)。这4个命令都是到systemctl
的符号链接,其行为受systemd控制。因此,在#电源管理中提到的规则对它们同样适用。
在由systemd控制的系统上,可通过init=
内核启动参数取消对System V的兼容性支持。详见/bin/init is in systemd-sysvcompat ?。
systemd-tmpfiles - 临时文件[编辑 | 编辑源代码]
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
配置文件也可能用于在启动时向特定文件中写入值。例如,要禁止系统从USB设备唤醒。在之前可使用/etc/rc.local
在启动时执行echo USBE > /proc/acpi/wakeup
,而现在可以这么做:
/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 <模块名>
检查该模块是否可通过设定模块参数来修改相应的选项。若有对应的模块参数,可通过/etc/modprobe.d目录中的配置文件设定相应参数以达到修改选项的目的。否则,将只能通过udev规则来在设备被识别时设定相应属性。小技巧[编辑 | 编辑源代码]
激活套接字[编辑 | 编辑源代码]
有一些包提供了一个 .socket 单元。例如,cups包 提供了一个 cups.socket
单元。如果 cups.socket
开启(并且 cups.service
保持关闭),systemd 并不会立刻启动 CUPS,它会监听适当的套接字并且当任何一个程序尝试连接 CUPS 服务中的一个时,systemd 会启动 cups.service
并且透明地将这些端口的控制权移交给 CUPS 进程。
GUI配置工具[编辑 | 编辑源代码]
- systemadm — 用于systemd单元的图形化浏览器。可显示单元列表,并能按类型筛选。
- SystemdGenie — 基于KDE的systemd管理工具。
在网络已连接后再启动某服务[编辑 | 编辑源代码]
如果需要将某服务延迟到网络已连接后再启动, 直接在 .service 文件中包含以下依赖项:
/etc/systemd/system/<单元名>.service
[Unit] ... Wants=network-online.target After=network-online.target ...
要使network-online.target
正确反映网络状态,必须启用所使用的网络管理器的网络等待服务。
- 若使用NetworkManager,
NetworkManager-wait-online.service
应该与NetworkManager.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.service
应该与systemd-networkd.service
一同启用。可通过systemctl is-enabled systemd-networkd-wait-online.service
进行检查。若服务没用启用,重新启用systemd-networkd.service
。
如果需要更为详细的解释,请查看网络配置同步点中的讨论。
若某服务需要执行DNS查询,其应该被排在nss-lookup.target
后:
/etc/systemd/system/<单元名>.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
引入nss-lookup.target
,并通过Before=nss-lookup.target
排在nss-lookup.target
之前。一般情况下,上述配置由本地DNS解析器完成。
检查哪一个正在运行的单元引入了nss-lookup.target
:
$ systemctl list-dependencies --reverse nss-lookup.target
默认启用新安装的单元[编辑 | 编辑源代码]
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)。
应用程序环境沙盒[编辑 | 编辑源代码]
可通过一个单元文件创建一个沙盒以在加固的虚拟环境中隔离应用程序及其进程。systemd充分利用namespaces,一系列允许/拒绝capabilities和cgroups,以通过可拓展的执行环境设置(systemd.exec(5))将进程禁锢在容器中。
将现有systemd单元文件进行沙盒化加固通常需要结合使用strace包,stderr和journalctl(1)等进行大量试验。因此,最好先在上游文档中搜索已完成的测试以作为试验的基础。
首先,最好通过以下命令获得特定单元可能的加固选项作为基础:
$ systemd-analyze security <单元>
关于如何使用 systemd 部署沙盒的一些示例:
CapabilityBoundingSet
定义了一个列表,以指明某个单元可以/不能使用哪些capabilities(7)。详见systemd.exec(5) § CAPABILITIES。- 例如,对
CAP_SYS_ADM
capability的控制应该是安全沙盒的目标:CapabilityBoundingSet=~ CAP_SYS_ADM
- 例如,对
通知失败的单元[编辑 | 编辑源代码]
要在服务故障时发出通知,可在相应服务的单元文件中添加OnFailure=
设置,这可通过#附加配置片段完成。若要在每一个服务的单元文件中添加该设置,可使用一个顶层附加配置片段,详见systemd.unit(5)。
为服务单元创建顶层附加配置片段:
/etc/systemd/system/service.d/toplevel-override.conf
[Unit] OnFailure=failure-notification@%n
这将向每个服务单元文件中添加OnFailure=failure-notification@%n
。若单元甲失败,会启动failure-notification@单元甲
来递送相关通知(或执行其它配置的动作)。
创建failure-notification@
模板单元:
/etc/systemd/system/failure-notification@.service
[Unit] Description=Send a notification about a failed systemd unit After=network.target [Service] Type=simple ExecStart=/path/to/failure-notification.sh %i
接下来可创建failure-notification.sh
脚本,定义执行的操作或发送通知的方式(mail,gotify,xmpp等) 。%i
将被替换为失败单元的名称并作为参数被传递给failure-notification.sh
脚本。
为防止failure-notification@.service
的实例启动失败时引发failure-notification@.service
实例的递归性启动,在failure-notification@.service
单元的配置目录下创建一个与顶层附加配置片段文件名相同的空的附加配置片段(该空的附加配置片段比全局附加配置片段优先级更高):
# mkdir -p /etc/systemd/system/failure-notification@.service.d # touch /etc/systemd/system/failure-notification@.service.d/toplevel-override.conf
疑难解答[编辑 | 编辑源代码]
寻找失败的单元[编辑 | 编辑源代码]
要寻找失败的systemd单元,执行:
$ systemctl --state=failed
可通过它们的日志查找失败的原因。详见systemd/Journal#过滤输出。
诊断系统启动问题[编辑 | 编辑源代码]
systemd有许多用于调试系统启动过程中的问题的选项。要捕获在systemd接管启动流程前的日志,参阅boot debugging。还可参阅此系统调试文档。
诊断一个服务[编辑 | 编辑源代码]
如果某个 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
启动journalctl观察该单元的日志。
关机/重启十分缓慢[编辑 | 编辑源代码]
如果关机十分缓慢(甚至跟死机了一样),很可能是某个拒不退出的服务在作怪。对于每个服务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 <单元名>
可能看不到任何输出,此时可转而使用进程的PID。例如,若systemd-modules-load.service
单元失败,通过systemctl status systemd-modules-load
得到的对应进程PID为123,以root身份执行journalctl -b _PID=123
或许就能得到日志输出。上述问题的原因是日志元数据(如_SYSTEMD_UNIT
、_COMM
)以异步方式被采集,且依赖于/proc
中相应进程的目录。修复该问题需要修改内核以通过socket提供需要的数据(类似于SCM_CREDENTIALS
)。简而言之,该问题是一个bug,只需记住由于systemd的设计,在启动后迅速失败的单元可能不会打印任何输出到系统日志中。
systemd-tmpfiles-setup.service在系统启动时失败[编辑 | 编辑源代码]
自systemd 219开始,/usr/lib/tmpfiles.d/systemd.conf
为/var/log/journal
下的目录指定了ACL选项。因此存放日志的文件系统必须启用ACL支持。
如何在存放/var/log/journal
的文件系统上启用ACL的说明见Access Control Lists#启用 ACL。
在远程主机上关闭救援(emergency)模式[编辑 | 编辑源代码]
当救援模式被触发时,机器不会连接网络,因此对于托管于Azure或Google Cloud的远程主机,或许需要关闭救援模式。
要关闭救援模式,屏蔽emergency.service
和emergency.target
。
参见[编辑 | 编辑源代码]
- Wikipedia:systemd
- Official web site
- systemd(1)
- Other distributions
- Lennart's blog story, update 1, update 2, update 3, summary
- Debug Systemd Services
- systemd for Administrators (PDF)
- How To Use Systemctl to Manage Systemd Services and Units
- Session management with systemd-logind
- Emacs Syntax highlighting for Systemd files
- Two part introductory article in The H Open magazine.