本文介绍 systemd 的常用工具、服务类型、unit 配置及服务启动顺序,帮助您理解和使用 systemd 进行系统服务管理。
systemd 常用工具
systemctl
systemctl 是最常用的 systemd 管理工具,主要命令如下:命令 | 说明 |
status | 查看 unit 状态 |
cat | 查看 unit 配置 |
start/restart/stop | 控制 unit 当前状态 |
set-property | 设置 unit 配置,例如 CPUAccounting、CPUShares 等 |
daemon-reload | 重新加载 manager 配置 |
list-units | 列出系统中的 unit 及其状态 |
list-dependencies | 以树状图展示各个 unit 之间的依赖关系 |
enable/disable | 设置/取消 unit 开机启动,等价于创建/删除 unit file 到 /etc/systemd/ 下的软链接 |
mask/unmask | 与 disable 类似,但 mask 后的 service 无法启动(即使被其他 unit require),等价于创建 unit file 到 /dev/null 的链接 |
journalctl
journalctl 用于查询 systemd 日志,常用参数如下:参数 | 说明 |
-u | 按 unit 查询 |
-b | 只显示本次启动日志 |
-k | 显示内核相关日志 |
例如,查看 dbus 服务相关日志:
journalctl -u dbus.service
journalctl -b 命令只能查看本次启动的日志。如果需要查询之前的启动日志,需将 journald 日志切换到磁盘存储(请参见日志管理相关文档),然后通过以下方式查询:使用
--list-boots 参数列出系统中所有启动记录:journalctl --list-boots
返回结果如下,其中
IDX 列为启动序号,负数表示历史启动记录,0 表示当前启动:IDX BOOT ID FIRST ENTRY LAST ENTRY-9 9d27b37bc8014865b0c20c957429dc31 Wed 2023-01-18 15:44:22 HKT Fri 2023-02-24 10:27:36 HKT-8 aa28b72e95074434a4a81b623c400e7e Tue 2023-02-28 16:35:24 HKT Tue 2023-02-28 16:45:08 HKT-7 12a71bb5bd9d4dec993d0e03e0cbe636 Mon 2023-03-06 10:46:43 HKT Mon 2023-03-06 11:07:43 HKT...
指定
IDX 序号即可查看对应启动的日志,例如查看序号为 -8 的启动日志:journalctl -b -8
systemd-analyze
systemd-analyze 是系统启动时间分析工具,主要子命令如下:子命令 | 说明 |
blame | 分析系统中各服务的启动时间 |
plot | 图形化显示系统中服务的启动顺序,可通过网页或图形编辑器打开 |
dump | 查看每个服务的状态、cgroup mask 等,比 systemctl status 更详细 |
blame
blame 命令分析系统中本次启动各服务的启动时间,默认按时间从长到短列出各服务的启动顺序:systemd-analyze blame
返回结果如下,按启动耗时从长到短排列:
8.814s kdump.service2.442s NetworkManager-wait-online.service944ms dev-vda1.device568ms sshd-keygen@rsa.service295ms initrd-switch-root.service214ms var-lib-nfs-rpc_pipefs.mount206ms dnf-makecache.service194ms dracut-initqueue.service143ms user@0.service135ms ipmi.service118ms initrd-parse-etc.service103ms chronyd.service99ms NetworkManager.service99ms systemd-logind.service92ms avahi-daemon.service88ms auditd.service71ms lvm2-monitor.service...
通过
blame 可以方便地排查系统启动时间。找到耗时服务后,结合该服务自身的日志或其他排查手段分析耗时原因。例如,上述输出中 kdump 服务启动时间最长,通过 strace 工具可以看到它最耗时的过程是加载 kdump 内核。systemd-cgls
systemd-cgls 用于查看系统中的 cgroup 层级。相比直接在 cgroup 目录下查看,systemd-cgls 会把每个 cgroup 中的进程都列出来,并且各个层级的关系更加清晰:systemd-cgls
返回结果如下,以树状结构展示各 cgroup 及其包含的进程:
├─user.slice│ └─user-0.slice│ → user.invocation_id: 183a492473c94efe8e3c4b1e1745f0b0│ → trusted.invocation_id: 183a492473c94efe8e3c4b1e1745f0b0│ ├─33878 sshd: root [priv]│ ├─33892 sshd: root@pts/0│ ├─33893 -bash│ ├─35517 systemd-cgls│ └─35518 less│ └─user@0.service│ └─init.scope│ ├─33882 /usr/lib/systemd/systemd --user│ └─33885 (sd-pam)├─init.scope│ └─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 31└─system.slice├─rngd.service│ └─685 /usr/sbin/rngd -f -x pkcs11 -x nist├─irqbalance.service│ └─683 /usr/sbin/irqbalance --foreground
systemd-nspawn
systemd-nspawn 的使用方式与 chroot 类似,但进入的目录需要是一个 OS tree 形式。如果目录是正确的 OS tree 形式,先查看目录内容确认:ls rootfs/
返回结果如下,即目录中包含完整的 OS 文件系统结构:
bin boot data dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
确认无误后,通过
systemd-nspawn 创建轻量级容器:说明:
如果目录不符合 OS tree 要求,执行以下命令将返回类似
Directory /root/rootfs doesn't look like it has an OS tree (/usr/ directory is missing). Refusing. 报错。systemd-nspawn -D ./rootfs
成功进入容器后,返回结果如下:
Spawning container rootfs on /root/rootfs.Press ^] three times within 1s to kill container.
如果需要模拟真实环境,还可增加
-b 选项在容器中启动 systemd。systemd 服务
服务开机启动
systemd 在启动时,会根据
/usr/lib/systemd/system-preset/ 以及 /usr/lib/systemd/user-preset/ 下的 preset 文件决定哪些服务开机启动。当前系统主要默认配置了以下几个配置文件:文件名 | 作用 |
90-default.preset | 控制默认启动的系统服务 |
99-default-disable.preset | 默认禁用所有服务 |
85-display-manager.preset | 控制桌面相关的服务是否需要自启动 |
preset 文件按照文件名决定执行顺序,前面的数字越小,优先级越高,越后执行。因此,在
99-default-disable.preset 中即便 disable 所有服务,仍可以在 90-default.preset 和 85-display-manager.preset 中开启所需的服务。systemd unit 种类
service
service 是最常接触的 unit,控制系统中的进程运行(包括 daemon 进程),例如 dbus.service、docker.service 等。通常通过 ExecStart= 指定程序运行的动作,ExecReload= 指定 systemctl reload 的动作,ExecStop= 指定停止 unit 的动作。使用
systemctl cat 查看 dbus.service 的 unit file 配置:systemctl cat dbus.service
返回结果如下,可以看到该 service 的完整配置,包括
[Unit]、[Service] 和 [Install] 三个配置段:# /usr/lib/systemd/system/dbus-broker.service[Unit]Description=D-Bus System Message BusDocumentation=man:dbus-broker-launch(1)DefaultDependencies=falseAfter=dbus.socketBefore=basic.target shutdown.targetRequires=dbus.socketConflicts=shutdown.target[Service]Type=notifySockets=dbus.socketOOMScoreAdjust=-900LimitNOFILE=16384ProtectSystem=fullPrivateTmp=truePrivateDevices=trueExecStart=/usr/bin/dbus-broker-launch --scope system --auditExecReload=/usr/bin/busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus ReloadConfig[Install]Alias=dbus.service
通过
systemctl status 可以查看 service 的状态信息,并打印该服务相关的 journal 日志。以 sshd 服务为例:systemctl status sshd
返回结果如下,包含服务加载状态、运行状态、主进程 PID、内存占用等信息:
● sshd.service - OpenSSH server daemonLoaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)Active: active (running) since Fri 2023-04-21 11:52:56 CST; 2 weeks 4 days agoDocs: man:sshd(8)man:sshd_config(5)Main PID: 1208 (sshd)Tasks: 1 (limit: 9183)Memory: 8.7MCPU: 199msCGroup: /system.slice/sshd.service└─1208 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
状态输出中各字段的含义如下:
服务名前面的圆点("●"):用不同的颜色和形状表示服务当前状态。
圆点颜色形状 | 表示的状态 |
"○" | inactive 或 maintenance |
"●"(绿色) | active |
"●"(白色) | deactivating |
"×" | failed 或 error |
"↻" | reloading |
Loaded 行:显示该服务是否被加载进内存。
loaded 表示已加载,error 表示加载失败,not-found 表示未找到 unit file,bad-setting 表示 unit file 配置错误,masked 表示服务被 mask。后面的路径为加载的 unit file 路径,再后面为当前该服务是否设置开机启动(enabled),vendor preset 表示发行版厂商是否将该服务预设为开机启动。Active 行:显示该服务的运行状态。括号前为当前状态,括号内为子状态。
当前状态取值如下:
状态 | 说明 |
active | unit 正在运行 |
inactive | unit 已经停止运行 |
activating | unit 正在启动中 |
deactivating | unit 正在停止中 |
failed | unit 启动失败 |
子状态取值如下:
子状态 | 说明 |
running | unit 正在运行 |
exited | unit 已经停止运行 |
waiting | unit 正在等待某些条件的发生,例如网络连接、文件系统挂载等 |
start-pre | unit 正在启动前的准备工作 |
start-post | unit 启动后的工作 |
stop-pre | unit 停止前的准备工作 |
stop-post | unit 停止后的工作 |
后面的时间为该服务最后一次启动的时间。
timer
timer 是定时器,在固定的时间触发激活其他 unit(默认是同名的 service)。通常通过 [Timer] 字段中的 OnActiveSec=/OnCalendar= 等配置项控制触发时间,详细说明可执行 man systemd.timer查看。例如,
sysstat-collect 中配置的 *:00/10 表示整点开始,每 10 分钟触发一次。使用 systemctl cat 查看其 timer 配置:systemctl cat sysstat-collect.timer
返回结果如下,可以看到
[Timer] 段中通过 OnCalendar=*:00/10 配置触发周期:# /usr/lib/systemd/system/sysstat-collect.timer# Activates activity collector every 10 minutes[Unit]Description=Run system activity accounting tool every 10 minutes[Timer]OnCalendar=*:00/10[Install]WantedBy=sysstat.service
通过
systemctl status 可以看到 timer 相比 service 多了 Trigger 和 Triggers 字段。Trigger 指下次触发的时间及剩余时间,Triggers 显示该计时器所激活的服务。查看 sysstat-collect.timer 的状态:systemctl status sysstat-collect.timer
返回结果如下,该示例中,timer 触发的服务是
sysstat-collect.service,8 分钟后再次触发。● sysstat-collect.timer - Run system activity accounting tool every 10 minutesLoaded: loaded (/usr/lib/systemd/system/sysstat-collect.timer; enabled; vendor preset: disabled)Active: active (waiting) since Thu 2023-05-11 20:04:47 CST; 1 week 0 days agoUntil: Thu 2023-05-11 20:04:47 CST; 1 week 0 days agoTrigger: Thu 2023-05-18 20:40:00 CST; 8min leftTriggers: ● sysstat-collect.service
mount
mount 用于控制系统中的挂载点,通常通过 [Mount] 字段中的 Where=/What=/Type= 控制挂载的位置、设备和类型。正常情况下只需配置 /etc/fstab,systemd-fstab-generator 会自动将 fstab 中的挂载点生成对应的 mount unit。使用
systemctl cat 查看已生成的 mnt.mount 配置:systemctl cat mnt.mount
返回结果如下,该 mount unit 由
systemd-fstab-generator 自动生成,配置内容与 /etc/fstab 中的挂载项对应:# /run/systemd/generator/mnt.mount# Automatically generated by systemd-fstab-generator[Unit]Documentation=man:fstab(5) man:systemd-fstab-generator(8)SourcePath=/etc/fstabBefore=local-fs.targetRequires=systemd-fsck@dev-vdb1.serviceAfter=systemd-fsck@dev-vdb1.serviceAfter=blockdev@dev-vdb1.target[Mount]What=/dev/vdb1Where=/mntType=ext4Options=noatime,acl,user_xattr
例如,在
/etc/fstab 中配置如下挂载项:cat /etc/fstab
返回结果如下,其中配置了
/dev/vdc1 挂载到 /data/ 目录:/dev/vdc1 /data/ ext4 noatime,acl,user_xattr 1 1
重启后(
systemd-fstab-generator 会在启动时执行),系统中会生成对应的 mount 服务,服务配置与 fstab 中配置相对应。首先使用 systemctl list-units 确认 mount 服务已生成:systemctl list-units -a | grep data
返回结果如下,可以看到
data.mount 已加载并处于 active 状态:data.mount loaded active mounted /data
然后执行以下命令查看其配置内容:
systemctl cat data.mount
返回结果如下,配置中的
What、Where、Type、Options 与 /etc/fstab 中的配置一致:# /run/systemd/generator/data.mount# Automatically generated by systemd-fstab-generator[Unit]Documentation=man:fstab(5) man:systemd-fstab-generator(8)SourcePath=/etc/fstabBefore=local-fs.targetRequires=systemd-fsck@dev-vdc1.serviceAfter=systemd-fsck@dev-vdc1.serviceAfter=blockdev@dev-vdc1.target[Mount]What=/dev/vdc1Where=/dataType=ext4Options=noatime,acl,user_xattr
automount
automount 与 mount 类似,区别是 automount 仅在用户访问对应目录时才挂载,而非像 mount 一样从启动后一直挂载。可以通过 autofs 使用 automount 能力。以下示例介绍如何使用 automount 服务。1. 安装 autofs 服务
dnf install -y autofs
2. 在
/etc/auto.master 文件中配置 automount 服务vim /etc/auto.master
在文件末尾添加以下内容:
/mnt/ /etc/auto.devices --timeout=10
其中,
/mnt/ 是挂载点所在的路径(不是设备挂载点),/etc/auto.devices 是设备挂载配置文件的路径,--timeout=10 表示设备挂载超时时间为 10 秒。3. 在
/etc/auto.devices 文件中配置需要挂载的设备vim /etc/auto.devices
在文件中添加需要挂载的设备信息:
mymnt -fstype=auto :/dev/vdb
其中,
mymnt 是挂载的目录,-fstype=auto 表示自动检测设备文件系统类型,:/dev/vdb 是设备的路径。4. 重启 autofs 服务
systemctl restart autofs
5. 验证 automount 功能
当需要访问
mymnt 设备时,访问 /mnt/ 目录下的 mymnt 目录,automount 服务会自动将 /dev/vdb 设备挂载到该目录下。注意:进入
/mnt 目录时,看不到 mymnt 目录,也无法通过 Tab 补全,但可以直接访问它。cd /mnt/mymnt
访问
/mnt/mymnt 目录后,automount 服务会自动将 /dev/vdb 设备挂载到该目录下。使用 lsblk 命令查看挂载状态:lsblk
返回结果如下,可以看到
/dev/vdb 已挂载到 /mnt/mymnt:NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTSsr0 11:0 1 18.8M 0 romzram0 251:0 0 8G 0 disk [SWAP]vda 252:0 0 100G 0 disk└─vda1 252:1 0 100G 0 part /vdb 252:16 0 200G 0 disk /mnt/mymntvdc 252:32 0 300G 0 disk└─vdc1 252:33 0 300G 0 part /data
如果一段时间内没有访问该目录,automount 服务会自动卸载设备,以释放系统资源。
target
target unit 与其他 unit 不同,它本身不执行任何操作,仅代表一个群组的 unit。target 通过 Requires=/After= 等 unit 依赖配置来控制每个群组 unit 的依赖关系和启动顺序,通过 systemctl list-dependencies 可以查看。以 multi-user.target 为例:systemctl list-dependencies multi-user.target
返回结果如下,以树状结构展示该 target 依赖的所有 unit:
multi-user.target● ├─acpid.service● ├─arpwatch.service● ├─atd.service● ├─auditd.service● ├─avahi-daemon.service● ├─chrony-wait.service● ├─chronyd.service● ├─crond.service● ├─gssproxy.service
系统启动时通过一个一个的 target 服务,按顺序拉起各个子系统所依赖的具体服务。例如,启动时系统只需拉起
default.target(本系统中为 multi-user.target 或 graphical.target),其他各个 target 只需在它们的 unit file 中指定:[Install]WantedBy=multi-user.target
即可实现在
multi-user.target 启动前启动。path
path unit 与 timer unit 类似,主要作用是当监控的路径改变时,触发同名的 service。通过 [Path] 字段中的 DirectoryNotEmpty=/PathChanged= 等配置项控制监控的路径及对应模式。使用
systemctl cat 查看系统自带的 systemd-ask-password-console.path 配置:systemctl cat systemd-ask-password-console.path
返回结果如下,可以看到
[Path] 段中通过 DirectoryNotEmpty= 监控 /run/systemd/ask-password 目录:# /usr/lib/systemd/system/systemd-ask-password-console.path[Unit]Description=Dispatch Password Requests to Console Directory WatchDocumentation=man:systemd-ask-password-console.path(8)DefaultDependencies=noConflicts=shutdown.target emergency.serviceAfter=plymouth-start.serviceBefore=paths.target shutdown.target cryptsetup.targetConditionPathExists=!/run/plymouth/pid[Path]DirectoryNotEmpty=/run/systemd/ask-passwordMakeDirectory=yes
以下示例介绍 path 服务的使用方法。
1. 创建
.path 单元文件在
/etc/systemd/system 目录下创建 my-path-monitor.path 文件,定义要监视的路径和触发条件:[Unit]Description=Monitor changes in /data/testpath directory[Path]PathModified=/data/testpathUnit=my-triggered-service.service[Install]WantedBy=multi-user.target
该
.path 单元文件将监视 /data/testpath 目录的更改。当该目录发生更改时,将触发名为 my-triggered-service.service 的服务。2. 创建关联的 service 单元文件
在
/etc/systemd/system 目录下创建 my-triggered-service.service 文件,定义服务的行为:[Unit]Description=Service triggered by path changes[Service]Type=oneshotExecStart=/usr/bin/echo "123"
该服务将在
.path 单元触发时执行 echo 123。3. 重新加载 systemd 配置
systemctl daemon-reload
4. 启动
.path 单元systemctl start my-path-monitor.path
5. 触发路径变更
rm -r /data/testpath
6. 验证服务是否被触发
查看
my-triggered-service.service 日志,该服务已被 path 服务触发:systemctl status my-triggered-service.service
返回结果如下,可以看到服务已被触发执行,状态为
inactive (dead) 表示已执行完毕:○ my-triggered-service.service - Service triggered by path changesLoaded: loaded (/etc/systemd/system/my-triggered-service.service; static)Active: inactive (dead) since Mon 2023-05-29 03:09:15 UTC; 9s agoTriggeredBy: ● my-path-monitor.pathProcess: 1227670 ExecStart=/usr/bin/echo 123 (code=exited, status=0/SUCCESS)Main PID: 1227670 (code=exited, status=0/SUCCESS)CPU: 1msMay 29 03:09:15 localhost.localdomain systemd[1]: Starting my-triggered-service.service - Service triggered by path changes...May 29 03:09:15 localhost.localdomain echo[1227670]: 123May 29 03:09:15 localhost.localdomain systemd[1]: my-triggered-service.service: Deactivated successfully.May 29 03:09:15 localhost.localdomain systemd[1]: Finished my-triggered-service.service - Service triggered by path changes.
socket
socket unit 与 timer/path 一样,配合 service unit 使用。通过 socket unit 指定 systemd 监听的 socket,当 socket 有写入时 systemd 唤醒该 unit 对应的 service unit。使用
systemctl cat 查看 acpid 服务及其 socket 的配置:systemctl cat acpid acpid.socket
返回结果如下,可以看到 service 通过
Requires=acpid.socket 依赖对应的 socket unit,socket unit 通过 ListenStream=/run/acpid.socket 指定监听地址:# /usr/lib/systemd/system/acpid.service[Unit]Description=ACPI Event DaemonDocumentation=man:acpid(8)Requires=acpid.socket[Service]StandardInput=socketEnvironmentFile=/etc/sysconfig/acpidExecStart=/usr/sbin/acpid -f $OPTIONS[Install]Also=acpid.socketWantedBy=multi-user.target# /usr/lib/systemd/system/acpid.socket[Unit]Description=ACPID Listen SocketDocumentation=man:acpid(8)[Socket]ListenStream=/run/acpid.socket[Install]WantedBy=sockets.target
以下示例介绍 socket 服务的使用方法。
1. 安装 nc 工具
dnf install nc -y
2. 创建 service 单元文件
在
/etc/systemd/system 目录下创建 socket_server.service 文件:[Unit]Description=Socket Server[Service]ExecStart=nc -l 12345User=nobodyGroup=nobody
3. 创建 socket 单元文件
在
/etc/systemd/system 目录下创建 socket_server.socket 文件:[Unit]Description=Socket Server Socket[Socket]ListenStream=12345Accept=yes[Install]WantedBy=sockets.target
该
.socket 单元文件将在 12345 端口上创建一个 TCP 套接字,并在接收到连接请求时激活 socket_server.service。4. 重新加载 systemd 配置
systemctl daemon-reload
5. 启动
.socket 单元systemctl start socket_server.socket
套接字已创建并开始监听连接。当有客户端连接到端口 12345 时,
socket_server.service 将被激活并执行 nc -l 12345。6. 验证连接
使用
telnet 或 nc(netcat)连接到端口 12345:telnet localhost 12345
swap
swap unit 管理 swap 分区挂载,与 mount 类似,通常也是通过 /etc/fstab 配置,由 systemd-fstab-generator 自动生成。1. 创建一个 swap 文件
fallocate -l 1G /swapfile
2. 将文件设置为 swap
mkswap /swapfile
3. 在
/etc/fstab 文件中添加 swap 配置在
/etc/fstab 文件末尾添加一行,指定 swap 分区或文件的路径、挂载点(使用 none)、文件系统类型(使用 swap)以及挂载选项(使用 sw),最后添加两个数字 0 0,表示不需要进行文件系统检查:/swapfile none swap sw 0 0
重启后,swap 分区或文件会自动启用,系统已自动创建对应的 swap 服务。使用
systemctl list-units 查看 swap 服务状态:systemctl list-units -a | grep swap
返回结果如下,可以看到
swapfile.swap 已加载并处于 active 状态:swapfile.swap loaded active active /swapfile
可以使用
free -h 或 swapon --show 命令查看 swap 的状态:free -h
返回结果如下,可以看到 Swap 总量为 1.0Gi,当前未使用:
total used free shared buff/cache availableMem: 7.5Gi 195Mi 7.1Gi 9Mi 210Mi 7.1GiSwap: 1.0Gi 0B 1.0Gi
device
device unit 是由 systemd-udevd 根据 /sys/ 和 /dev/ 下的设备创建的 unit,主要作用是控制设备和设备之间以及设备和其他 unit 之间的依赖关系。使用
systemctl list-dependencies 查看 mnt.mount 的依赖关系,可以看到其中包含 dev-vdb1.device:systemctl list-dependencies mnt.mount
返回结果如下,
mnt.mount 依赖对应的块设备 unit dev-vdb1.device:mnt.mount● ├─-.mount● ├─dev-vdb1.device● ├─system.slice○ └─systemd-fsck@dev-vdb1.service
slice
slice 类似 target,也是一组 unit 的代表,但 slice 用于在 cgroup 中进行资源控制。系统默认将 unit 分为 user.slice(用户会话)、system.slice(系统进程)、machine.slice(容器和虚拟机进程)。使用
tree 命令查看系统中的 cgroup 层级结构:tree -d /sys/fs/cgroup/
返回结果如下,可以看到各个 service 分别归属于
system.slice 和 user.slice:/sys/fs/cgroup/├── dev-hugepages.mount├── dev-mqueue.mount├── init.scope├── proc-fs-nfsd.mount├── proc-sys-fs-binfmt_misc.mount├── sys-fs-fuse-connections.mount├── sys-kernel-config.mount├── sys-kernel-debug.mount├── sys-kernel-tracing.mount├── system.slice│ ├── NetworkManager.service│ ├── acpid.service│ ├── arpwatch.service│ ├── atd.service│ ├── auditd.service│ ├── avahi-daemon.service│ ├── chronyd.service│ ├── systemd-logind.service│ ├── systemd-udevd.service│ │ └── udev│ ├── systemd-userdbd.service│ ├── tmp.mount│ └── var-lib-nfs-rpc_pipefs.mount└── user.slice└─user-0.slice├── session-1702.scope├── session-22304.scope
scope
scope 与 slice/target 类似,不同的是 scope unit 仅可通过 systemd dbus 接口创建,且 scope unit 中的进程不会自行创建子进程。unit 配置
systemd 支持的 unit 配置参数数量庞大,大部分问题都与配置有关。这里以 CPU 资源控制相关配置为例,CPU 的资源控制配置主要由以下几种方式决定。
default 配置
systemd 有一个 unit 配置表
ConfigTableItem,表中的配置项都会有默认初始值。如果后续没有手动配置,这些属性会使用代码中的初始值,例如 CPUAccounting 默认为 false。可以在 src/core/main.c 文件中查看:static int parse_config_file(void) {const ConfigTableItem items[] = {{ "Manager", "LogLevel", config_parse_level2, 0, NULL },{ "Manager", "LogTarget", config_parse_target, 0, NULL },.....
manager 配置文件
manager 配置文件位于以下路径:
/etc/systemd/system.conf/etc/systemd/user.conf/etc/systemd/system.conf.d//etc/systemd/user.conf.d/
unit file
unit 自己的配置文件一般在
/usr/lib/systemd/system/ 下。可以通过 systemctl cat a.service 查看具体内容。通过 systemctl set-property 命令手动设置属性时,最终会在 /etc/systemd/ 下生成对应 unit 的额外配置文件。以
rsyslog.service 为例,使用 systemctl set-property 设置 CPU 配额为 50%:systemctl set-property rsyslog.service CPUQuota=50%
然后使用
systemctl show 查看该属性的配置路径:systemctl show rsyslog.service | grep CPUQuota
返回结果如下,可以看到
DropInPaths 指向了自动生成的配置文件路径:DropInPaths=/etc/systemd/system.control/rsyslog.service.d/50-CPUQuota.conf
可以看到最终生成
/etc/systemd/system.control/rsyslog.service.d/50-CPUQuota.conf 额外配置文件。查看该文件内容:cat /etc/systemd/system.control/rsyslog.service.d/50-CPUQuota.conf
返回结果如下,文件中记录了通过
set-property 设置的 CPUQuota=50% 配置:# This is a drop-in unit file extension, created via "systemctl set-property"# or an equivalent operation. Do not edit.[Service]CPUQuota=50%
服务启动顺序
systemd 服务的启动顺序通过配置相互依赖关系实现。unit 与 unit 之间的依赖关系分为多种情况,包括自动生成的依赖和显式配置的依赖。
自动生成依赖
自动生成的依赖分为两种:隐式依赖和默认依赖。
隐式依赖:通过 unit
Type 和 Unit 的配置决定。例如 dbus.service 的 Type=dbus,会隐式依赖 dbus.socket(等价于 Requires= 以及 After=dbus.socket)。默认依赖:与隐式依赖类似,但可以通过
DefaultDependencies= 选项控制开关。例如 A.target 里配置了 Wants= 或者 Requires=B,A 会默认被配置一个 After=B。所有 target 都会默认配置 Conflicts=shutdown.target 和 Before=shutdown.target,以保证关机前所有 target 已退出。Triggers/TriggeredBy 会默认配置在 socket/path/mount 等对应的 service 中。显式配置依赖
显式配置的依赖关系主要有 6 种(两两配对,逻辑相同但行为相反):
依赖关系 | 说明 |
Wants/WantedBy | 弱依赖。A Wants= B,A 启动时会尝试拉起 B,如果 B 启动失败,A 继续启动。也可以通过在 unit file 外创建 .wants 目录实现同样效果 |
Requires/RequiredBy | 强依赖。A Requires= B,A 启动时会尝试拉起 B,如果 B 启动失败,A 不会启动。也可以通过创建 .requires/ 目录实现同样效果 |
BindsTo/BoundBy | 更强依赖。与 Requires 不同的是,A BindsTo= B,如果 A 关闭了,B 也会关闭,而 Requires 时 B 不受影响 |
Before/After | 启动顺序。A Before= B,A 启动后 B 才能启动。关闭流程相反:A Before= B 时,如果 A 和 B 都需要关闭,B 先关闭,A 才会关闭 |
PartOf/ConsistsOf | 与 Requires 相似,但 PartOf 只影响关闭和重启动作。A PartOf= B,B 被关闭或重启时,A 也会被关闭或重启 |
Conflicts/ConflictedBy | A Conflicts= B,启动 A 就会关闭 B |
例如,查看 rsyslog 服务是由谁拉起的,通过
list-dependencies 命令的 --reverse 参数查看反向依赖:systemctl list-dependencies rsyslog --reverse
返回结果如下,可以看到
rsyslog.service 被 multi-user.target 拉起,而 multi-user.target 又被 graphical.target 拉起:rsyslog.service● └─multi-user.target○ └─graphical.target
之所以有如此依赖关系,是因为 rsyslog 服务的 unit file 中有如下配置。使用
systemctl cat 查看其配置:systemctl cat rsyslog.service
返回结果如下,可以看到
[Install] 段中通过 WantedBy=multi-user.target 指定了依赖关系:# /usr/lib/systemd/system/rsyslog.service[Unit]...[Install]WantedBy=multi-user.target...