systemctl 命令详解与参数实测(Ubuntu 22.04)
以 Ubuntu 22.04(systemd 249)实测逐参数讲解 systemctl:is-system-running 与 status 逐字段解读、start/stop/restart/reload、enable/disable/mask/unmask(含 mask 踩坑)、list-units/list-unit-files/list-dependencies/list-timers、show/cat、get-default/set-default、daemon-reload,附实战场景与常见问题。
systemctl 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 systemctl 的每一个常用参数——is-system-running、status、start/stop/restart/reload、enable/disable、mask/unmask、list-units、list-unit-files、list-dependencies、show、cat、daemon-reload、list-timers、get-default/set-default 等,全部命令与回显已在 Ubuntu 22.04(systemd 249)实测通过。
一、环境信息
本文所有命令均在以下环境实测,回显逐字取自真实执行结果:
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 内核版本 | 6.8.0-138-generic |
| init 系统版本 | systemd 249(249.11-0ubuntu3.23) |
| 主机名 | b8307d66e0c6(回显中主机名部分会原样出现) |
| 系统时区 | UTC(回显中的时间戳均为 UTC 时间) |
| 执行用户 | root |
| 测试数据目录 | /root/demo/systemctl |
| 自定义单元文件 | /etc/systemd/system/demo-svc.service、/etc/systemd/system/demo-fail.service |
验证环境可用性的第一条命令(若输出不是 running,说明 systemd 未完全就绪,后续实验结果可能失真):
$ systemctl is-system-running
running
二、命令概览
2.1 语法与测试数据
systemctl 的基本语法为「选项 + 子命令 + 单元名」,其中子命令是主体(status、start、enable 等),选项修饰输出与行为:
systemctl [OPTIONS...] COMMAND ...
为了让全部参数有真实观测对象,本文在 /etc/systemd/system/ 下创建了两个演示单元。第一个是贯穿全文的自定义服务 demo-svc.service,它以 30 秒为周期通过 logger 向系统日志写入一条 heartbeat,便于观察服务启停与日志联动:
$ cat /etc/systemd/system/demo-svc.service
[Unit]
Description=Demo Service for systemctl practice
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash -c 'while :; do logger -t demo-svc "heartbeat"; sleep 30; done'
Restart=on-failure
[Install]
WantedBy=multi-user.target
第二个是必然启动失败的单元 demo-fail.service(ExecStart 直接执行 /bin/false),用于实测 failed 状态与 --failed 列表。它没有 [Install] 段,因此无法被 enable,只能手动 start:
[Unit]
Description=Demo Failing Service for systemctl practice
[Service]
Type=simple
ExecStart=/bin/false
💡 技术拓展:systemd 管理的"单元"不止服务一种。常见单元类型对比——
类型 含义 典型例子 service 守护进程/服务 nginx.service、sshd.service target 一组单元的集合点(类似运行级别) multi-user.target、graphical.target timer 定时触发其他单元(替代 cron) apt-daily.timer socket 按需监听端口触发服务 sshd.socket mount / automount 挂载点 / 按需挂载 tmp.mount path 路径变化触发服务 systemd-ask-password-console.path slice 资源分组(cgroup 层级) system.slice scope 外部创建的进程组 session-1.scope device / swap 设备与交换分区 dev-sda1.device 本文实测对象是 service 类型单元,这也是日常运维中占比最高的类型。
2.2 --help 全量输出(分段照抄)
systemctl --help 是本文最重要的速查来源,官方原文较长,下面按主干分段照抄(原文中 list-machines、exit、-M --machine 等少数面向"本机多实例管理"的行与本文主线无关,已省略并在此注明,其余逐字照抄)。
Unit Commands(单元操作命令):
systemctl [OPTIONS...] COMMAND ...
Query or send control commands to the system manager.
Unit Commands:
list-units [PATTERN...] List units currently in memory
list-sockets [PATTERN...] List socket units currently in memory,
ordered by address
list-timers [PATTERN...] List timer units currently in memory,
ordered by next elapse
is-active PATTERN... Check whether units are active
is-failed PATTERN... Check whether units are failed
status [PATTERN...|PID...] Show runtime status of one or more units
show [PATTERN...|JOB...] Show properties of one or more
units/jobs or the manager
cat PATTERN... Show files and drop-ins of specified units
help PATTERN...|PID... Show manual for one or more units
list-dependencies [UNIT...] Recursively show units which are required
or wanted by the units or by which those
units are required or wanted
start UNIT... Start (activate) one or more units
stop UNIT... Stop (deactivate) one or more units
reload UNIT... Reload one or more units
restart UNIT... Start or restart one or more units
try-restart UNIT... Restart one or more units if active
reload-or-restart UNIT... Reload one or more units if possible,
otherwise start or restart
try-reload-or-restart UNIT... If active, reload one or more units,
if supported, otherwise restart
isolate UNIT Start one unit and stop all others
kill UNIT... Send signal to processes of a unit
clean UNIT... Clean runtime, cache, state, logs or
configuration of unit
freeze PATTERN... Freeze execution of unit processes
thaw PATTERN... Resume execution of a frozen unit
set-property UNIT PROPERTY=VALUE... Sets one or more properties of a unit
bind UNIT PATH [PATH] Bind-mount a path from the host into a
unit's namespace
mount-image UNIT PATH [PATH [OPTS]] Mount an image from the host into a
unit's namespace
service-log-level SERVICE [LEVEL] Get/set logging threshold for service
service-log-target SERVICE [TARGET] Get/set logging target for service
reset-failed [PATTERN...] Reset failed state for all, one, or more
units
Unit File Commands(单元文件命令):
Unit File Commands:
list-unit-files [PATTERN...] List installed unit files
enable [UNIT...|PATH...] Enable one or more unit files
disable UNIT... Disable one or more unit files
reenable UNIT... Reenable one or more unit files
preset UNIT... Enable/disable one or more unit files
based on preset configuration
preset-all Enable/disable all unit files based on
preset configuration
is-enabled UNIT... Check whether unit files are enabled
mask UNIT... Mask one or more units
unmask UNIT... Unmask one or more units
link PATH... Link one or more units files into
the search path
revert UNIT... Revert one or more unit files to vendor
version
add-wants TARGET UNIT... Add 'Wants' dependency for the target
on specified one or more units
add-requires TARGET UNIT... Add 'Requires' dependency for the target
on specified one or more units
edit UNIT... Edit one or more unit files
get-default Get the name of the default target
set-default TARGET Set the default target
Job / Environment / Manager State Commands:
Job Commands:
list-jobs [PATTERN...] List jobs
cancel [JOB...] Cancel all, one, or more jobs
Environment Commands:
show-environment Dump environment
set-environment VARIABLE=VALUE... Set one or more environment variables
unset-environment VARIABLE... Unset one or more environment variables
import-environment VARIABLE... Import all or some environment variables
Manager State Commands:
daemon-reload Reload systemd manager configuration
daemon-reexec Reexecute systemd manager
log-level [LEVEL] Get/set logging threshold for manager
log-target [TARGET] Get/set logging target for manager
service-watchdogs [BOOL] Get/set service watchdog state
System Commands(系统级命令,含开关机):
System Commands:
is-system-running Check whether system is fully running
default Enter system default mode
rescue Enter system rescue mode
emergency Enter system emergency mode
halt Shut down and halt the system
poweroff Shut down and power-off the system
reboot Shut down and reboot the system
kexec Shut down and reboot the system with kexec
switch-root ROOT [INIT] Change to a different root file system
suspend Suspend the system
hibernate Hibernate the system
hybrid-sleep Hibernate and suspend the system
suspend-then-hibernate Suspend the system, wake after a period of
time, and hibernate
Options(常用选项):
Options:
-h --help Show this help
--version Show package version
--system Connect to system manager
--user Connect to user service manager
-H --host=[USER@]HOST Operate on remote host
-t --type=TYPE List units of a particular type
--state=STATE List units with particular LOAD or SUB or ACTIVE state
--failed Shortcut for --state=failed
-p --property=NAME Show only properties by this name
-P NAME Equivalent to --value --property=NAME
-a --all Show all properties/all units currently in memory,
including dead/empty ones. To list all units installed
on the system, use 'list-unit-files' instead.
-l --full Don't ellipsize unit names on output
-r --recursive Show unit list of host and local containers
--reverse Show reverse dependencies with 'list-dependencies'
--with-dependencies Show unit dependencies with 'status', 'cat',
'list-units', and 'list-unit-files'.
--job-mode=MODE Specify how to deal with already queued jobs, when
queueing a new job
-T --show-transaction When enqueuing a unit job, show full transaction
--show-types When showing sockets, explicitly show their type
--value When showing properties, only print the value
--check-inhibitors=MODE
Specify if checking inhibitors before shutting down,
sleeping or hibernating
-i Shortcut for --check-inhibitors=no
--kill-who=WHO Whom to send signal to
-s --signal=SIGNAL Which signal to send
--what=RESOURCES Which types of resources to remove
--now Start or stop unit after enabling or disabling it
--dry-run Only print what would be done
Currently supported by verbs: halt, poweroff, reboot,
kexec, suspend, hibernate, suspend-then-hibernate,
hybrid-sleep, default, rescue, emergency, and exit.
-q --quiet Suppress output
--wait For (re)start, wait until service stopped again
For is-system-running, wait until startup is completed
--no-block Do not wait until operation finished
--no-wall Don't send wall message before halt/power-off/reboot
--no-reload Don't reload daemon after en-/dis-abling unit files
--legend=BOOL Enable/disable the legend (column headers and hints)
--no-pager Do not pipe output into a pager
--no-ask-password Do not ask for system passwords
--global Enable/disable/mask default user unit files globally
--runtime Enable/disable/mask unit files temporarily until next
reboot
-f --force When enabling unit files, override existing symlinks
When shutting down, execute action immediately
--preset-mode= Apply only enable, only disable, or all presets
--root=PATH Enable/disable/mask unit files in the specified root
directory
-n --lines=INTEGER Number of journal entries to show
-o --output=STRING Change journal output mode (short, short-precise,
short-iso, short-iso-precise, short-full,
short-monotonic, short-unix,
verbose, export, json, json-pretty, json-sse, cat)
--firmware-setup Tell the firmware to show the setup menu on next boot
--boot-loader-menu=TIME
Boot into boot loader menu on next boot
--boot-loader-entry=NAME
Boot into a specific boot loader entry on next boot
--plain Print unit dependencies as a list instead of a tree
--timestamp=FORMAT Change format of printed timestamps.
'pretty' (default): 'Day YYYY-MM-DD HH:MM:SS TZ
'us': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU TZ
'utc': 'Day YYYY-MM-DD HH:MM:SS UTC
'us+utc': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC
--read-only Create read-only bind mount
--mkdir Create directory before mounting, if missing
--marked Restart/reload previously marked units
See the systemctl(1) man page for details.
2.3 版本信息
$ systemctl --version
systemd 249 (249.11-0ubuntu3.23)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified
逐段解读:
- 第一行
systemd 249 (249.11-0ubuntu3.23):systemd 主版本 249,括号内是 Ubuntu 22.04 打包的完整修订号(含安全补丁版本 .23); - 第二行 是编译特性清单:
+表示编译时启用该特性,-表示未启用。实测环境启用了 PAM(可插拔认证)、AUDIT(审计)、SELINUX、APPARMOR、SECCOMP(系统调用过滤)等;未启用的有 IDN、PWQUALITY、P11KIT、QRENCODE、XKBCOMMON 等; - 行尾
default-hierarchy=unified表示默认 cgroup 层级为 v2 统一层级(cgroups v2)。
💡 说明:判断某功能是否可用时先看这一行,例如 -PWQUALITY 说明本环境编译时未带密码质量校验模块。
三、参数逐项实测
3.1 is-system-running:检查系统整体状态
本节先确认 systemd 管理器自身的健康度,这是所有 systemctl 操作的前置检查。该命令无参数,输出管理器当前状态并配合退出码判断:
$ systemctl is-system-running; echo rc=$?
running
rc=0
running 表示系统已完全启动、所有单元处于正常状态,退出码 0。实测中该命令还会返回其他状态,含义对照如下:
| 输出 | 含义 | 退出码 |
|---|---|---|
| running | 系统完全就绪 | 0 |
| degraded | 已运行但有单元失败(systemctl --failed 可见) | 1 |
| maintenance | 处于 rescue/emergency 等维护模式 | 1 |
| initializing / starting | 早期启动阶段,尚未就绪 | 1 |
| stopping | 正在关机 | 1 |
| offline | 管理器未运行 | 1 |
| unknown | 无法通信(权限不足等) | 4 |
💡 说明:degraded 是生产服务器最常见的"异常"状态——系统仍可用,但存在失败单元。运维巡检脚本可直接用退出码分支处理。
3.2 status:查看单元运行状态(逐字段解读)
status 是使用频率最高的子命令。先启动 demo-svc.service 再查看其状态:
$ systemctl start demo-svc.service; echo rc=$?
rc=0
$ systemctl status demo-svc.service --no-pager
● demo-svc.service - Demo Service for systemctl practice
Loaded: loaded (/etc/systemd/system/demo-svc.service; disabled; vendor preset: enabled)
Active: active (running) since Thu 2026-10-15 21:58:04 UTC; 2s ago
Main PID: 124 (bash)
Tasks: 2 (limit: 7196)
Memory: 568.0K
CPU: 3ms
CGroup: /system.slice/demo-svc.service
├─124 /bin/bash -c "while :; do logger -t demo-svc \"heartbeat\"; sleep 30; done"
└─127 sleep 30
Oct 15 21:58:04 b8307d66e0c6 systemd[1]: Started Demo Service for systemctl practice.
逐字段解读:
- 行首圆点
●:状态标记。●绿色表示 active,○空心表示 inactive,×表示 failed; Loaded: loaded (...; disabled; vendor preset: enabled):单元定义已加载,路径为 /etc/systemd/system/demo-svc.service;disabled表示当前未设置开机自启(enable 后此处变 enabled);vendor preset: enabled表示发行版预设策略允许该单元开机自启;Active: active (running) since Thu 2026-10-15 21:58:04 UTC; 2s ago:高级状态 active + 低级状态 running,启动时刻与已运行时长。服务崩溃后此处会变为failed (Result: exit-code);Main PID: 124 (bash):主进程 PID 及进程名。跟踪主进程是 systemd 判断服务存活的依据(Type=simple 时即 ExecStart 的第一个进程);Tasks: 2 (limit: 7196):单元内任务数 2(bash 与其子进程 sleep),括号内是 TaskMax 限制;Memory: 568.0K:当前内存占用(cgroup 统计值);CPU: 3ms:累计 CPU 消耗;CGroup: /system.slice/demo-svc.service及下方进程树:该单元的完整进程树,逐个列出 PID 与命令行,可直接看到 bash 主进程与 sleep 子进程;- 日志片段:status 末尾自动附最近 10 条该单元的 journal 日志(可用 -n N 调整,如
systemctl status demo-svc -n 50)。
status 也接受 PID 作为参数(systemctl status 124 可反查某进程属于哪个单元),或不带参数显示整机状态概要。
3.3 start / stop:启动与停止单元
start/stop 是最基础的生命周期操作。实测顺序为:stop → is-active 确认 → start → 确认:
$ systemctl stop demo-svc.service; echo rc=$?
rc=0
$ systemctl is-active demo-svc.service; echo rc=$?
inactive
rc=3
$ systemctl start demo-svc.service; echo rc=$?
rc=0
要点:
- stop 后
is-active输出inactive且退出码为 3(非 0),这是设计行为而非报错——is-active 用退出码表达状态,供脚本判断; - 对已停止的单元再执行 stop、对已运行的单元再执行 start 都会静默成功(幂等),退出码仍为 0;
- start 是异步的:systemd 排入启动任务即返回。多数 simple 类型服务瞬间完成,若需阻塞等待可用
systemctl start xxx --wait。
3.4 restart / try-restart:重启的差异
restart 对停止状态的单元会直接启动;try-restart 只在单元已运行时才重启,停止状态则什么都不做。实测(当前服务处于运行态):
$ systemctl restart demo-svc.service; echo rc=$?
rc=0
$ systemctl try-restart demo-svc.service; echo rc=$?
rc=0
两者对运行中服务效果一致,差别在停止态:restart 会把服务拉起来,try-restart 不会。因此配置管理工具(如 Ansible handler)惯用 try-restart——配置变更时只重启"本来就在跑"的服务,不把人为停止的服务意外拉起。
3.5 reload / reload-or-restart:重载不支持 reload 的服务会报错
reload 让服务在不重启进程的情况下重新读取配置(服务需实现 ExecReload)。demo-svc.service 未定义 ExecReload,实测报错形态:
$ systemctl reload demo-svc.service; echo rc=$?
Failed to reload demo-svc.service: Job type reload is not applicable for unit demo-svc.service.
rc=3
报错关键字是 Job type reload is not applicable。对比之下,reload-or-restart 会自动降级——支持 reload 就 reload,不支持就 restart:
$ systemctl reload-or-restart demo-svc.service; echo rc=$?
rc=0
$ systemctl is-active demo-svc.service; echo rc=$?
active
rc=0
💡 说明:对 nginx(支持
nginx -s reload)、sshd 等实现了 ExecReload 的服务,reload 不中断连接;对未实现的服务,reload-or-restart 等价于 restart,会有短暂中断。写运维脚本时先确认目标服务是否支持 reload。
3.6 is-active / is-enabled / is-failed:三个查询命令与退出码对照
这三个命令用于脚本化判断,核心是用退出码而非输出文本做分支。当前 demo-svc 处于运行中但未 enable:
$ systemctl is-active demo-svc.service; echo rc=$?
active
rc=0
$ systemctl is-enabled demo-svc.service; echo rc=$?
disabled
rc=1
$ systemctl is-failed demo-svc.service; echo rc=$?
active
rc=1
退出码语义汇总(本组实测确认):
| 命令 | 输出 | 退出码 | 脚本含义 |
|---|---|---|---|
| is-active | active | 0 | 服务运行中 |
| is-active | inactive | 3 | 服务未运行 |
| is-active | failed | 3 | 服务失败(可用 is-failed 细分) |
| is-enabled | enabled | 0 | 已设开机自启 |
| is-enabled | disabled | 1 | 未设开机自启 |
| is-enabled | masked | 1 | 已被封印(见 3.11) |
| is-failed | failed | 0 | 服务失败("是失败的"返回真) |
| is-failed | active | 1 | 服务未失败 |
注意 is-failed 的退出码方向与 is-active 相反:它回答"是否失败",failed 时返回 0(真)。
3.7 show:查看单元的全部属性(-p 提取单属性)
status 是给人看的状态摘要,show 则输出机器可读的全部属性(每行一个 KEY=VALUE)。先用 -p 提取三个关心的属性:
$ systemctl show demo-svc.service -p MainPID -p ActiveState -p SubState
MainPID=147
ActiveState=active
SubState=running
不加 -p 时输出全部属性,实测共 242 行,以下为首部 12 行节选:
$ systemctl show demo-svc.service --no-pager | head -12
Type=simple
Restart=on-failure
NotifyAccess=none
RestartUSec=100ms
TimeoutStartUSec=1min 30s
TimeoutStopUSec=1min 30s
TimeoutAbortUSec=1min 30s
TimeoutStartFailureMode=terminate
TimeoutStopFailureMode=terminate
RuntimeMaxUSec=infinity
WatchdogUSec=0
WatchdogTimestamp=n/a
$ systemctl show demo-svc.service --no-pager | wc -l
242
要点:
- show 的属性名与单元文件的指令名对应但格式不同(单元文件写
Restart=on-failure,属性同为Restart=on-failure;时间类指令以 USec 结尾,值带单位); - 常用单属性组合:
-p MainPID取主进程号、-p ActiveState取 active/failed 等、-p ExecMainStartTimestamp取启动时刻; - 配合
--value可只输出值不输出键名,便于$(...)直接赋值给脚本变量:systemctl show demo-svc -p MainPID --value。
3.8 cat:查看单元文件内容与 drop-in 覆盖
cat 输出单元文件的实际生效内容,并自动带上文件路径注释。若存在 drop-in 覆盖(/etc/systemd/system/xxx.service.d/*.conf),也会一并列出:
$ systemctl cat demo-svc.service
# /etc/systemd/system/demo-svc.service
[Unit]
Description=Demo Service for systemctl practice
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash -c 'while :; do logger -t demo-svc "heartbeat"; sleep 30; done'
Restart=on-failure
[Install]
WantedBy=multi-user.target
排障时用 cat 而不是直接 cat 文件的原因:systemctl cat 展示的是 systemd 实际加载到的全部片段(含 drop-in),能发现"改了 A 文件却被 B 片段覆盖"的问题。
3.9 enable / disable:设置与取消开机自启(符号链接机制)
enable 的本质是在目标的 .wants 目录下创建单元文件的符号链接。实测 enable 并用 ls 验证链接确实生成:
$ systemctl enable demo-svc.service; echo rc=$?
Created symlink /etc/systemd/system/multi-user.target.wants/demo-svc.service → /etc/systemd/system/demo-svc.service.
rc=0
$ ls -l /etc/systemd/system/multi-user.target.wants/
total 0
lrwxrwxrwx 1 root root 36 Oct 15 21:58 demo-svc.service -> /etc/systemd/system/demo-svc.service
lrwxrwxrwx 1 root root 40 Sep 24 14:05 e2scrub_reap.service -> /lib/systemd/system/e2scrub_reap.service
lrwxrwxrwx 1 root root 47 Oct 15 18:06 networkd-dispatcher.service -> /lib/systemd/system/networkd-dispatcher.service
lrwxrwxrwx 1 root root 36 Oct 15 18:06 remote-fs.target -> /lib/systemd/system/remote-fs.target
lrwxrwxrwx 1 root root 44 Oct 15 18:06 systemd-resolved.service -> /lib/systemd/system/systemd-resolved.service
$ systemctl is-enabled demo-svc.service; echo rc=$?
enabled
rc=0
要点:
- demo-svc 的 [Install] 段写的是
WantedBy=multi-user.target,因此链接创建在 /etc/systemd/system/multi-user.target.wants/ 下——哪个 target 想要它,链接就放进哪个 target 的 .wants 目录; - disable 则移除该符号链接:
$ systemctl disable demo-svc.service; echo rc=$?
Removed /etc/systemd/system/multi-user.target.wants/demo-svc.service.
rc=0
$ systemctl is-enabled demo-svc.service; echo rc=$?
disabled
rc=1
- enable/disable 不影响当前运行状态——enable 后服务不会自动启动,disable 后服务也不会自动停止,两者只决定"下次开机"。
3.10 enable --now:设置自启并立即启动
--now 让 enable 顺带执行 start,一步完成"开机自启 + 立即运行"。实测先停止服务再执行 enable --now:
$ systemctl stop demo-svc.service
$ systemctl enable --now demo-svc.service; echo rc=$?
Created symlink /etc/systemd/system/multi-user.target.wants/demo-svc.service → /etc/systemd/system/demo-svc.service.
rc=0
$ systemctl is-active demo-svc.service; echo rc=$?
active
rc=0
对应的还有 disable --now(取消自启并停止)。生产部署新服务时推荐用 enable --now,避免"enable 完忘了 start"或"start 完忘了 enable"两类遗漏。
3.11 mask / unmask:封印与解封(含 systemd 249 的实测踩坑)
mask 比disable 更彻底:它把单元文件路径替换为指向 /dev/null 的符号链接,使 systemd 连加载都不可能,连手动 start 都会拒绝,用于"确保这个服务永远不会再被拉起"(例如屏蔽与 systemd 冲突的 sysv 遗留脚本)。
实测踩坑:对 /etc/systemd/system/ 下已存在实体文件的单元直接 mask,systemd 249 会拒绝:
$ systemctl mask demo-svc.service; echo rc=$?
Failed to mask unit: File /etc/systemd/system/demo-svc.service already exists.
rc=1
$ systemctl is-enabled demo-svc.service; echo rc=$?
enabled
rc=0
原因是 mask 要创建符号链接 /etc/systemd/system/demo-svc.service → /dev/null,而该路径已被真实单元文件占用。systemd 249 的正确做法是先把单元文件移出搜索路径再 mask(被移走前单元已加载进内存,mask 仍可执行):
$ mv /etc/systemd/system/demo-svc.service /tmp/demo-svc.service.bak
$ systemctl mask demo-svc.service; echo rc=$?
Created symlink /etc/systemd/system/demo-svc.service → /dev/null.
rc=0
$ ls -l /etc/systemd/system/demo-svc.service
lrwxrwxrwx 1 root root 9 Oct 15 21:58 /etc/systemd/system/demo-svc.service -> /dev/null
$ systemctl is-enabled demo-svc.service; echo rc=$?
masked
rc=1
$ systemctl start demo-svc.service; echo rc=$?
Failed to start demo-svc.service: Unit demo-svc.service is masked.
rc=1
Failed to start demo-svc.service: Unit demo-svc.service is masked. 就是 mask 生效的直接证据——即使有人手动 start 也会被拒绝。
两个重要实测发现:
其一,mask 不停止已在运行的实例。mask 后立即查看 status,Loaded 行显示 masked,但 Active 行仍是 active (running):
$ systemctl status demo-svc.service --no-pager | head -6
● demo-svc.service
Loaded: masked (Reason: Unit demo-svc.service is masked.)
Active: active (running) since Thu 2026-10-15 21:58:45 UTC; 56ms ago
Main PID: 352 (bash)
CPU: 3ms
CGroup: /system.slice/demo-svc.service
所以"封印"要彻底需要两步:systemctl stop xxx && systemctl mask xxx(stop 先行,mask 拦截后续所有启动)。
其二,unmask 只移除 /dev/null 链接,不恢复被移走的单元文件,需要手工还原:
$ systemctl unmask demo-svc.service; echo rc=$?
Removed /etc/systemd/system/demo-svc.service.
rc=0
$ mv /tmp/demo-svc.service.bak /etc/systemd/system/demo-svc.service
$ systemctl is-enabled demo-svc.service; echo rc=$?
disabled
rc=1
$ systemctl start demo-svc.service; echo rc=$?
rc=0
$ systemctl is-active demo-svc.service; echo rc=$?
active
rc=0
💡 说明:若单元文件安装在 /lib/systemd/system/(发行版包管理器维护),/etc 下没有本地副本,直接
systemctl mask即可成功,无需上述移走步骤——踩坑仅发生在单元文件本身位于 /etc/systemd/system/ 的场景。
3.12 --failed 与 reset-failed:失败单元的查看与复位
先启动必然失败的 demo-fail.service(ExecStart=/bin/false),start 命令本身返回 0(任务已受理),失败要靠 --failed 查看:
$ systemctl start demo-fail.service; echo rc=$?
rc=0
$ systemctl --failed --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
● demo-fail.service loaded failed failed Demo Failing Service for systemctl practice
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.
1 loaded units listed.
$ systemctl is-failed demo-fail.service; echo rc=$?
failed
rc=0
--failed 等价于 --state=failed,是排障入口之一:服务器 degraded 时先跑这条。失败状态会一直保留(供人查看原因),处理完毕后用 reset-failed 清除失败标记:
$ systemctl reset-failed demo-fail.service; echo rc=$?
rc=0
$ systemctl --failed --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
0 loaded units listed.
reset-failed 不带参数时清除全部单元的失败标记。
3.13 list-units:列出内存中的单元(--type / --state / --all)
list-units(不带参数时的默认行为就是它)列出 systemd 当前加载的单元。--type=service 只看服务,输出节选(实测保留前 14 行):
$ systemctl list-units --type=service --no-pager | head -14
UNIT LOAD ACTIVE SUB DESCRIPTION
dbus.service loaded active running D-Bus System Message Bus
demo-svc.service loaded active running Demo Service for systemctl practice
getty@tty1.service loaded active running Getty on tty1
networkd-dispatcher.service loaded active running Dispatcher daemon for systemd-networkd
systemd-binfmt.service loaded active exited Set Up Additional Binary Formats
systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running User Login Management
systemd-remount-fs.service loaded active exited Remount Root and Kernel File Systems
systemd-resolved.service loaded active running Network Name Resolution
systemd-sysctl.service loaded active exited Apply Kernel Variables
systemd-sysusers.service loaded active exited Create Static Device Nodes in /dev
systemd-tmpfiles-setup-dev.service loaded active exited Create Static Device Nodes in /dev
表格五列含义:UNIT 单元名、LOAD 是否成功加载、ACTIVE 高级状态(active/inactive/failed)、SUB 低级状态(running/exited/dead 等随类型而异)、DESCRIPTION 描述。注意 SUB 有两种常见值:running 表示常驻进程服务,exited 表示一次性执行完成的服务(如 systemd-sysctl),后者正常存在并非故障。
--state=running 进一步缩小到"正在跑进程"的服务,实测共 7 个:
$ systemctl list-units --type=service --state=running --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
dbus.service loaded active running D-Bus System Message Bus
demo-svc.service loaded active running Demo Service for systemctl practice
getty@tty1.service loaded active running Getty on tty1
networkd-dispatcher.service loaded active running Dispatcher daemon for systemd-networkd
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running User Login Management
systemd-resolved.service loaded active running Network Name Resolution
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.
7 loaded units listed.
默认 list-units 只显示已加载且活动(或失败)的单元;--all 把未活动单元也列出来。两个总数对比:
$ systemctl list-units --type=service --no-pager | wc -l
23
$ systemctl list-units --type=service --all --no-pager | wc -l
64
💡 说明:list-units 的范围是"内存中"的单元(包括运行过一次的);要看系统里"安装了哪些"单元文件,用下一节的 list-unit-files。
3.14 list-unit-files:列出已安装单元文件与自启状态(STATE 列解读)
list-unit-files 扫描单元文件搜索目录,展示每个单元文件的自启状态,这正是回答"哪些服务会开机自启"的命令。输出节选(前 5 行):
$ systemctl list-unit-files --type=service --no-pager | head -5
UNIT FILE STATE VENDOR PRESET
apt-daily-upgrade.service static -
apt-daily.service static -
autovt@.service alias -
console-getty.service enabled-runtime disabled
STATE 列取值不止 enabled/disabled 两种,用 grep 抽出含 masked 与 alias 的行对照观察(完整实测回显):
$ systemctl list-unit-files --type=service --no-pager | grep -E 'masked|alias'
autovt@.service alias -
cryptdisks-early.service masked enabled
cryptdisks.service masked enabled
dbus-org.freedesktop.hostname1.service alias -
dbus-org.freedesktop.locale1.service alias -
dbus-org.freedesktop.login1.service alias -
dbus-org.freedesktop.resolve1.service alias -
dbus-org.freedesktop.timedate1.service alias -
dbus-org.freedesktop.timesync1.service alias -
hwclock.service masked enabled
kmod.service alias -
procps.service alias -
rc.service masked enabled
rcS.service masked enabled
x11-common.service masked enabled
用 grep 定位本文的两个演示单元,可见 enable/disable 状态的变化直接反映在 STATE 列(demo-svc 此刻为 enabled,demo-fail 无 [Install] 段为 static):
$ systemctl list-unit-files --type=service --no-pager | grep -E '^demo|UNIT FILE'
UNIT FILE STATE VENDOR PRESET
demo-fail.service static -
demo-svc.service enabled enabled
$ systemctl list-unit-files --type=service --no-pager | wc -l
101
STATE 列全部取值解读:
| STATE | 含义 |
|---|---|
| enabled | 已创建 .wants 符号链接,开机自启 |
| disabled | 未创建链接,不自启 |
| static | 单元无 [Install] 段,不能 enable/disable,只能被其他单元依赖或手动启动 |
| masked | 已被 /dev/null 链接封印 |
| alias | 该名是另一单元的别名 |
| enabled-runtime | 仅本次运行期内 enabled(--runtime 创建,重启失效) |
| generated | 由 systemd 生成(如 /etc/fstab 挂载项转出) |
| transient | 运行时临时创建(如 systemd-run) |
| indirect | 间接启用(自身未被链接,但它 enable 时会带动别的单元) |
VENDOR PRESET 列是发行版预设策略,供 systemctl preset 使用,运维中少直接操作。
3.15 list-dependencies:查看依赖树(--reverse 反向)
list-dependencies 递归展示单元的依赖。demo-svc 声明了 After=network.target(排序依赖)且运行于 system.slice,其实际依赖树为:
$ systemctl list-dependencies demo-svc.service --no-pager
demo-svc.service
● ├─system.slice
● └─sysinit.target
● ├─dev-hugepages.mount
● ├─dev-mqueue.mount
○ ├─kmod-static-nodes.service
● ├─proc-sys-fs-binfmt_misc.automount
● ├─sys-fs-fuse-connections.mount
○ ├─sys-kernel-config.mount
● ├─sys-kernel-debug.mount
● ├─sys-kernel-tracing.mount
● ├─systemd-ask-password-console.path
● ├─systemd-binfmt.service
○ ├─systemd-boot-system-token.service
● ├─systemd-journal-flush.service
● ├─systemd-journald.service
○ ├─systemd-machine-id-commit.service
○ ├─systemd-modules-load.service
○ ├─systemd-pstore.service
○ ├─systemd-random-seed.service
● ├─systemd-sysctl.service
● ├─systemd-sysusers.service
○ ├─systemd-timesyncd.service
● ├─systemd-tmpfiles-setup-dev.service
● ├─systemd-tmpfiles-setup.service
● ├─systemd-update-utmp.service
● ├─cryptsetup.target
● ├─local-fs.target
● │ └─systemd-remount-fs.service
● ├─swap.target
● └─veritysetup.target
行首实心圆 ● 表示该依赖单元当前 active,空心圆 ○ 表示 inactive——这棵树同时也是"服务启动前拉起了哪些东西"的答案。
--reverse 反转视角,看"谁依赖我"。demo-svc 被 multi-user.target 拉起(因其处于运行状态),multi-user.target 又是 graphical.target 的组成:
$ systemctl list-dependencies demo-svc.service --reverse --no-pager
demo-svc.service
● └─multi-user.target
● └─graphical.target
排障场景:服务启动失败先看正向依赖里哪个环节 inactive;想评估"停掉它会牵连谁"则看反向依赖。
3.16 list-timers:查看定时器
list-timers 按下次触发时间排序列出全部 timer 单元,实测输出(本机共 7 个):
$ systemctl list-timers --all --no-pager
NEXT LEFT LAST PASSED UNIT ACTIVATES
Fri 2026-10-16 00:00:00 UTC 2h 1min left n/a n/a dpkg-db-backup.timer dpkg-db-backup.service
Fri 2026-10-16 06:53:05 UTC 8h left n/a n/a apt-daily-upgrade.timer apt-daily-upgrade.service
Fri 2026-10-16 07:39:15 UTC 9h left Thu 2026-10-15 21:58:04 UTC 2s ago motd-news.timer motd-news.service
Fri 2026-10-16 14:44:40 UTC 16h left n/a n/a apt-daily.timer apt-daily.service
Fri 2026-10-16 18:22:50 UTC 20h left Thu 2026-10-15 18:22:50 UTC 3h 35min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Sun 2026-10-18 03:10:44 UTC 2 days left n/a n/a e2scrub_all.timer e2scrub_all.service
n/a n/a n/a n/a fstrim.timer fstrim.service
7 timers listed.
逐列解读:NEXT 下次触发时刻、LEFT 倒计时、LAST 上次触发时刻、PASSED 距上次触发多久。n/a 表示该定时器自安装以来尚未触发过(如 dpkg-db-backup.timer),fstrim.timer 的 NEXT 也是 n/a(其调度依赖每周窗口,本机暂未计算到)。UNIT 是定时器单元名,ACTIVATES 是它到点后启动的服务。排查"定时任务没跑"时:NEXT 是 n/a 说明 OnCalendar 未到或定时器未启用,LAST 与预期不符则去 journalctl -u <timer> 查原因。
3.17 -t help:列出全部单元类型
--type 的合法取值不用背,-t help 直接列出(实测 11 种):
$ systemctl -t help
Available unit types:
service
mount
swap
socket
target
device
automount
timer
path
slice
scope
这 11 种类型与本文 2.1 节的单元类型拓展表一一对应,任何一种都可以用于 --type=TYPE 过滤。
3.18 get-default / set-default:查看与设置默认启动目标
默认启动目标决定开机进入的模式(等价旧体系的"运行级别")。实测查看、切换到 multi-user.target(多用户字符界面,等价 runlevel 3)、再还原为 graphical.target(图形界面,等价 runlevel 5)的完整过程:
$ systemctl get-default
graphical.target
$ systemctl set-default multi-user.target; echo rc=$?
Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target.
rc=0
$ systemctl get-default
multi-user.target
$ systemctl set-default graphical.target; echo rc=$?
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target → /lib/systemd/system/graphical.target.
rc=0
$ systemctl get-default
graphical.target
机制与 enable 一样是符号链接:set-default 重建 /etc/systemd/system/default.target 链接指向目标 target。服务器一般设 multi-user.target 以节省图形栈资源;set-default 只影响下次开机,本次切换模式用 systemctl isolate multi-user.target(会立即停止不属于该 target 的单元,慎在生产使用)。
3.19 daemon-reload:重载单元文件配置
修改或新建单元文件后,systemd 内存中的缓存不会自动更新,必须执行 daemon-reload。它不重启任何服务,只让 systemd 重新扫描单元文件:
$ systemctl daemon-reload; echo rc=$?
rc=0
执行后 rc=0 无输出。两条铁律:改完任何 .service/.timer 文件先 daemon-reload 再操作;否则 status 会提示警告且 start 可能仍在用旧定义。
3.20 -l 与 --no-pager:控制输出格式
-l(--full)让输出不省略截断(超长单元名与命令行默认会以 … 截断);--no-pager 禁止进入 less 分页器,输出直接打印到标准输出——写脚本采集输出时必须加:
$ systemctl status demo-svc.service -l --no-pager | head -5
● demo-svc.service - Demo Service for systemctl practice
Loaded: loaded (/etc/systemd/system/demo-svc.service; disabled; vendor preset: enabled)
Active: active (running) since Thu 2026-10-15 21:58:04 UTC; 2s ago
Main PID: 124 (bash)
Tasks: 2 (limit: 7196)
交互终端下 status/list-units 默认进入分页器(按 q 退出),此时无需手动加 --no-pager。
3.21 -H:在远程主机上执行
-H/--host=[USER@]HOST 让所有 systemctl 子命令通过 SSH 在远端主机执行,例如 systemctl -H root@10.0.0.5 status nginx 等价于登录到 10.0.0.5 后执行,适合集中管理少量机器。前提是本机到目标主机的 SSH 免密已配置好。该参数依赖网络与 SSH 环境,不在本文实测范围内展开。
3.22 poweroff / reboot:关机与重启(生产环境慎用)
systemctl poweroff 关机、systemctl reboot 重启,均属 System Commands 组。这两个命令属于危险操作,本文不做实测,仅作说明:生产环境执行前必须确认没有在线用户与未落盘数据,可加 --no-wall 跳过全用户广播(一般不建议),也可用 --dry-run 预演将触发的动作。日常运维更安全的替代是联系变更窗口并提前 wall 通知在线用户。
四、实战示例
4.1 场景一:部署一个自定义服务并设置开机自启
完整流程五步:写单元文件 → daemon-reload → enable --now → status 验证 → is-enabled 验证。以 demo-svc 为例(单元文件内容见 2.1 节):
$ systemctl daemon-reload
$ systemctl enable --now demo-svc.service
Created symlink /etc/systemd/system/multi-user.target.wants/demo-svc.service → /etc/systemd/system/demo-svc.service.
$ systemctl status demo-svc.service --no-pager | head -4
● demo-svc.service - Demo Service for systemctl practice
Loaded: loaded (/etc/systemd/system/demo-svc.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2026-10-15 22:04:16 UTC; 2s ago
Main PID: 446 (bash)
$ systemctl is-enabled demo-svc.service
enabled
Loaded 行为 enabled、Active 为 active (running),说明自启与运行双双到位。收尾清理同样五步:disable、stop、删除单元文件、daemon-reload、grep 确认单元已消失:
$ systemctl disable demo-svc.service
Removed /etc/systemd/system/multi-user.target.wants/demo-svc.service.
$ systemctl stop demo-svc.service
$ systemctl daemon-reload
$ systemctl list-unit-files --type=service --no-pager | grep '^demo'; echo rc=$?
rc=1
grep 无输出且 rc=1,确认单元文件已彻底移除。
4.2 场景二:排查一个启动失败的服务
三段式定位:--failed 找出失败单元 → is-failed 确认 → journalctl 查失败原因。以 demo-fail 为例:
$ systemctl start demo-fail.service
$ systemctl --failed --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
● demo-fail.service loaded failed failed Demo Failing Service for systemctl practice
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.
1 loaded units listed.
$ systemctl status demo-fail.service --no-pager | head -5
× demo-fail.service - Demo Failing Service for systemctl practice
Loaded: loaded (/etc/systemd/system/demo-fail.service; static)
Active: failed (Result: exit-code) since Thu 2026-10-15 22:04:35 UTC; 3ms ago
Process: 511 ExecStart=/bin/false (code=exited, status=1/FAILURE)
Main PID: 511 (code=exited, status=1/FAILURE)
status 的 failed 形态直接给出 Result: exit-code 与 status=1/FAILURE,据此可判断是进程主动退出(退出码 1)而非被信号杀死。原因修复后 reset-failed 清除失败状态:
$ systemctl reset-failed demo-fail.service
$ systemctl --failed --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
0 loaded units listed.
4.3 场景三:巡检脚本中用退出码判断服务健康
is-active/is-enabled 的退出码可直接用于 shell 条件判断,下面的巡检片段对单个服务先查运行态再查自启态(示例中服务运行正常,两个分支都走"健康"路径):
$ systemctl is-active demo-svc.service > /dev/null; if [ $? -eq 0 ]; then echo "demo-svc: RUNNING"; else echo "demo-svc: DOWN"; fi
demo-svc: RUNNING
$ systemctl is-enabled demo-svc.service > /dev/null; if [ $? -eq 0 ]; then echo "demo-svc: AUTO-START ON"; else echo "demo-svc: AUTO-START OFF"; fi
demo-svc: AUTO-START ON
配合 list-units --state=running 可生成全机运行服务清单,再对每个关键服务套用上述判断。
4.4 场景四:彻底屏蔽一个不需要的服务
目标是"取消自启 + 禁止任何人手动启动"。对单元文件位于 /lib 的服务两步即可(stop + mask);对单元文件位于 /etc 的服务,参照 3.11 节先移走文件再 mask。验证方式是 is-enabled 输出 masked 且 start 报 Unit ... is masked.(实测回显见 3.11 节)。解除时先 unmask 再恢复单元文件,最后 daemon-reload。
4.5 场景五:把服务器切换为纯字符界面启动
图形界面在服务器上是多余的资源消耗。两步:设置默认目标为 multi-user.target(下次开机生效),并用 isolate 立即切换(本次生效):
$ systemctl set-default multi-user.target
Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target.
$ systemctl get-default
multi-user.target
isolate 会立即停掉 graphical.target 独有的单元(图形会话将被终止),务必在确认无人使用图形会话时执行。回切方法:systemctl set-default graphical.target(实测回显见 3.18 节)。
五、注意事项与常见问题
Q1:systemctl reload 提示 "Job type reload is not applicable",是坏了吗? 不是。说明该服务未定义 ExecReload(本组 demo-svc 实测即如此,见 3.5 节),reload 语义不存在。需要刷新配置时改用 restart,或写运维脚本时统一用 reload-or-restart 自动降级。
Q2:mask 报 "Failed to mask unit: File ... already exists" 怎么办? systemd 249 对单元文件实体位于 /etc/systemd/system/ 的单元拒绝直接 mask(符号链接无处安放,实测见 3.11 节)。先把单元文件 mv 出搜索路径再 mask,unmask 后记得把文件 mv 回去并 daemon-reload。单元文件在 /lib/systemd/system/ 下的可直接 mask。
Q3:mask 了服务,为什么进程还在跑? mask 只拦截"后续的启动请求",不会停止已运行实例(3.11 节实测:mask 后 status 仍显示 active (running))。彻底下线必须先 stop 再 mask。
Q4:is-active 返回退出码 1/3,脚本是不是出错了? 不是。这些查询命令用退出码表达状态:is-active 的 0=active、3=inactive/failed;is-failed 的 0=failed、1=active。脚本判断用退出码,别用输出文本比对。
Q5:enable 之后服务怎么没启动? enable 只创建开机自启的符号链接,不作用于当前运行状态。要"自启 + 立即运行"用 enable --now(3.10 节实测)。
Q6:改了单元文件不生效,start 还是旧行为?
systemd 只在 daemon-reload 时重新读取单元文件。改完先 systemctl daemon-reload,再 restart。新建单元文件后不 reload 直接 start 会报错,本组实测形态:
$ systemctl start demo-fail.service
Failed to start demo-fail.service: Unit demo-fail.service not found.
看到 Unit ... not found 先执行 daemon-reload 再重试即可。
Q7:Loaded 行的 static 是什么意思? 该单元没有 [Install] 段(本组 demo-fail 实测即 static),无法 enable/disable,只能被其他单元依赖或手动 start。这是正常状态不是缺陷。
Q8:生产环境可以直接 systemctl poweroff/reboot 吗? 危险操作,必须走变更流程。执行前用 who 查在线用户、确认关键服务已优雅停止;--dry-run 可预演。永远不要在不明会话状态下随手执行。
六、参考链接
- systemctl(1) 手册(man7.org):https://man7.org/linux/man-pages/man1/systemctl.1.html
- systemd 官方 systemctl 文档:https://www.freedesktop.org/software/systemd/man/systemctl.html
- systemd.unit 单元文件语法:https://www.freedesktop.org/software/systemd/man/systemd.unit.html
- systemd.service 服务单元配置:https://www.freedesktop.org/software/systemd/man/systemd.service.html
- systemd.target 目标单元:https://www.freedesktop.org/software/systemd/man/systemd.target.html
- Ubuntu 22.04 systemd 源码包页:https://launchpad.net/ubuntu/+source/systemd