0 · 先把「服务」拆成三个维度 不建立这个模型,后面全是死记
考纲只写了一句「启动和停止服务,配置服务以在系统启动时自动启动」,但它背后是三个互相独立的维度。绝大多数错误判断,都是把这三个混成一个「服务开没开」。
| 维度 | 问的问题 | 查法 | 取值 |
|---|---|---|---|
| 有没有 | 这个 unit 存不存在 | systemctl cat X | 有 / No such file |
| 开机启不启 | 下次开机跑不跑 | is-enabled | enabled / disabled / masked / static |
| 现在跑不跑 | 此刻有没有进程 | is-active | active / inactive / failed |
start / stop / restart 只动进程(影响 is-active);enable / disable 只动链接(影响 is-enabled);mask / unmask 封锁的是「还能不能被启动」。1 · 看清现状(纯只读)一个字节都不要改
systemctl is-enabled crond sshd firewalld systemctl is-active crond sshd firewalld systemctl status crond --no-pager -l
is-enabled 三行 enabled;is-active 三行 active;status 头部能读到两行关键信息 —— Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; preset: enabled) 和 Active: active (running)。--no-pager(不是 --no-paper),是 -l(小写字母 L,不是数字 1)。打错时 systemd 只会甩一句 unrecognized option,不给你任何提示。preset: 是什么:装机时按厂商策略自动设定的默认值(Rocky/RHEL 一般是 enabled)。它只解释「这个状态是怎么来的」,不影响你现在的操作。ls -l /etc/systemd/system/multi-user.target.wants/crond.service systemctl disable crond systemctl is-enabled crond systemctl is-active crond ls -l /etc/systemd/system/multi-user.target.wants/crond.service
crond.service -> /usr/lib/systemd/system/crond.service 的软链接;disable 打印 Removed "/etc/systemd/system/multi-user.target.wants/crond.service".;之后是 disabled + active;最后 ls 报 No such file or directory。wants/ 目录里那个指向 unit 文件的软链接。它不碰 /usr/lib/systemd/system/crond.service,也不碰正在运行的进程。所以「服务还在跑」和「不自启」同时成立,很正常。systemctl stop crond systemctl is-enabled crond systemctl is-active crond systemctl start crond systemctl enable crond systemctl is-enabled crond; systemctl is-active crond
stop 之后是 enabled + inactive (dead),status 里 Main PID 那行消失、出现 Duration: 2d 2h 50min;start 之后 Main PID 换成一个新数字(原来的进程已经退出);最后两行回到 enabled + active。systemctl stop sshd 会当场断开,而且它不会自己回来。start / restart 都是「新建进程」,不是「恢复原进程」。看到 PID 变了不是异常,是必然。systemctl mask crond systemctl is-enabled crond systemctl enable crond systemctl start crond systemctl status crond --no-pager -l | head -6 ls -l /etc/systemd/system/crond.service
mask 打印 Created symlink /etc/systemd/system/crond.service -> /dev/null.;is-enabled 返回 masked;enable 失败并说 Failed to enable unit: Unit file /etc/systemd/system/crond.service is masked.;start 失败并说 Failed to start crond.service: Unit crond.service is masked.;status 显示 Loaded: masked (Reason: Unit crond.service is masked.) 但 Active: active (running)。enable 要先读 unit 的 [Install] 段,才知道该往哪个 target 的 wants/ 里建链接。mask 之后这个路径指向 /dev/null,内容是空的,读不到 WantedBy= —— 于是无从下手。顺序只能是 unmask → enable。/etc/systemd/system/multi-user.target.wants/crond.service 是开机自启的链接;/etc/systemd/system/crond.service 是 mask 的遮盖。在 wants/ 目录里 ls 看到还指向 /usr/lib,不代表 mask 没生效。crond.service: Current command vanished from the unit file —— 这是 systemd 发现 ExecStart= 不见了,只警告、不终止。systemctl unmask crond systemctl is-enabled crond systemctl is-active crond ls -l /etc/systemd/system/crond.service systemctl status crond --no-pager -l | head -4
unmask 打印 Removed "/etc/systemd/system/crond.service".;随后 enabled + active;ls 报 No such file or directory(遮盖已删);status 回到 Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; ...)。unmask 删的只是 /etc/systemd/system/crond.service -> /dev/null 这个遮盖。wants/ 里的自启链接从 mask 到 unmask 全程没被碰过,所以一揭开就还是 enabled。systemctl list-unit-files --type=service --no-legend | wc -l systemctl list-unit-files --type=service --state=enabled --no-legend | wc -l systemctl list-units --type=service --state=running --no-legend | wc -l systemctl list-units --type=service --all --no-legend | wc -l
list-unit-files 数的是磁盘上的 unit 文件(静态视角,含没加载的);list-units 数的是已经加载进内存的 unit(运行时视角,默认只列 active,--all 才全看)。所以 16 和 23 分属两个集合,本来就不该相等。list-unit-files --state=running 会输出 0 unit files listed.、stderr 一片空白、退出码 1。因为 running 是 sub-state,list-unit-files 不认;但如果你接了 | wc -l,退出码换成 wc 的 0,你只会看到一个「0」,看着像「没有服务在跑」。正确做法:
--state=running 只配 list-units;要判成败,别接管道。systemctl show -p MainPID --value crond systemctl restart crond systemctl show -p MainPID --value crond systemctl show -p CanReload crond systemctl reload crond systemctl show -p MainPID --value crond
restart 前后 PID 一定不同(实测 7565 → 7922);reload 前后 PID 一定相同(实测 8025 → 8025);CanReload=yes。restart = stop + start,旧进程被杀、新进程拉起,PID 必然变。reload 不杀进程,只按 unit 里的 ExecReload= 给现有进程发信号(crond 是 /bin/kill -HUP $MAINPID),进程本身没死,PID 自然不变。ExecReload= 就不支持。先查 systemctl show -p CanReload X(yes/no)。硬要 reload 会得到 Failed to reload X: Job type reload is not applicable for unit X.,退出码 3(不是 1)。本机实测:crond、sshd 是 yes,chronyd、dnf-makecache 是 no。restart;只是重读配置(日志级别、白名单)→ reload,不断连接。systemctl disable --now crond systemctl is-enabled crond; systemctl is-active crond systemctl enable --now crond systemctl is-enabled crond; systemctl is-active crond
disable --now 之后 disabled + inactive;enable --now 之后 enabled + active。会同时打印 Removed ... / Created symlink ...。enable --now = enable + start;disable --now = disable + stop。--now 只负责「顺手把另一个维度也做了」,没有新增语义。所以
enable --now 不等于 restart:服务已经在跑时,它不会再起一次,PID 不变。systemctl enable --now httpd 就够,别写成两条。systemctl is-enabled dnf-makecache.service; echo "exit=$?" systemctl cat dnf-makecache.service | grep -n Install; echo "grep_exit=$?" systemctl enable dnf-makecache.service; echo "enable_exit=$?" systemctl is-enabled dnf-makecache.service ls /etc/systemd/system/multi-user.target.wants/ | grep makecache; echo "ls_exit=$?"
is-enabled 输出 static、exit=0;grep Install 无输出、exit=1(证明没有 [Install] 段);enable 往 stderr 打一大段 The unit files have no installation config (WantedBy=, RequiredBy=, Also=, Alias= ...),但 enable_exit=0;最后 is-enabled 仍是 static,ls | grep 找不到链接。[Install] 段。而 enable 的全部工作就是读 [Install] 里的 WantedBy= 去建链接 —— 没有这段,它不知道往哪儿建,所以「没法被 enable」。这种 unit 只能:被别的 unit 用 Wants=/Requires= 拉起来,或者手动 start。systemctl enable 退出码 0 ≠ 启用成功。因为它认为「这个 unit 不需要安装配置」不算错误。判断是否真生效,只能看 is-enabled 的结果和 wants/ 里有没有新链接。反过来:
is-enabled 对 enabled 和 static 都返回 0,光看退出码区分不出来,必须看文本。systemctl list-unit-files --type=service --state=enable; echo "exit=$?" systemctl list-unit-files --type=service --state=enable | wc -l; echo "pipe_exit=$?" systemctl list-unit-files --type=service --state=enabled --no-legend | wc -l
0 unit files listed.,但第一条 exit=1、第二条 pipe_exit=0;第三条是 23(正确拼写)。enabled,不是动词 enable。而 --state 对取值不做白名单校验 —— 它拿这个字符串去过滤,匹配不上任何 unit,就是 0 个,stderr 一个字节都没有(实测 err_size=0)。它不会告诉你「你拼错了」。--state=running 也是 0 个(T6)。记住一句话 —— systemctl 的 --state 是过滤条件,不是校验器;零结果先怀疑自己写错,别怀疑机器。2 · target 与默认启动目标 考纲:配置系统自动启动进入某个特定目标
systemctl get-default ls -l /etc/systemd/system/default.target readlink -f /etc/systemd/system/default.target systemctl cat multi-user.target | head -20
get-default 打印 multi-user.target;ls -l 显示 /etc/systemd/system/default.target -> /usr/lib/systemd/system/multi-user.target;readlink -f 把这条链一路解析到底。default.target。它本身没有进程、没有 ExecStart=,只是一条软链接,指向真正的目标(这里是 multi-user.target)。systemd 顺着链接找到它,再按它的 Wants=/Requires= 把一整片服务拉起来。所以「换默认运行级别」在文件系统上就是换一条软链接,没有别的魔法。
/etc/systemd/system/default.target 是管理员可改的(set-default 写这里);/usr/lib/systemd/system/ 是发行版自带的。前者优先级更高。你改 /usr/lib 里的东西,下次包升级会被覆盖。systemctl set-default multi-user.target; echo "exit=$?" ls -l /etc/systemd/system/default.target
exit=0,链接的时间戳不变(说明没重建)。set-default 发现目标已经是它,就直接返回。想确认真的生效,不能看「有没有输出」,要看 get-default 和 ls -l 的结果 —— 无输出既可能是成功,也可能是没执行。systemctl set-default graphical.target; echo "exit=$?" ls -l /etc/systemd/system/default.target; systemctl get-default systemctl list-dependencies graphical.target --no-pager | head -20 systemctl set-default multi-user.target; echo "exit=$?" systemctl get-default
Removed "/etc/systemd/system/default.target". 再 Created symlink ... → ...graphical.target.;get-default 立刻跟着变;依赖树第一层是 display-manager.service、systemd-update-utmp-runlevel.service、multi-user.target。最后必须切回 multi-user.target。graphical.target 在 multi-user.target 之上再加一个图形登录管理器(display-manager.service)。所以设成 graphical 不会丢掉 multi-user 那一层 —— 它是超集,不是替代品。/usr/lib/systemd/system/display-manager.service 不存在(Minimal 没装桌面),list-units --all 里它是 not-found inactive dead。所以即使设成 graphical,开机也只是「想拉但拉不到」,照样进 multi-user。不要用「有没有图形界面」判断 set-default 成没成功,要看软链接。systemctl isolate multi-user.target; echo "exit=$?" systemctl get-default ls -l /etc/systemd/system/default.target systemctl show -p AllowIsolate multi-user.target rescue.target
isolate 无输出、exit 0;get-default 和链接都不变;AllowIsolate 两个都是 yes。isolate 只切换当前运行目标(会停掉不属于新目标的服务),不碰 default.target 链接;set-default 只改下次开机用的链接,不动当前运行状态。两者正交。rescue.target 只拉起 sysinit.target + rescue.service,网络、sshd 全部不在里面 —— 从 SSH 执行会立刻失联。这类操作只能在 VMware 控制台做。手册里只观察依赖树,不动手。3 · journald:日志在哪、怎么筛、怎么留 考纲:定位并解读系统日志 / 保留系统日志
ls /run/systemd/journal/ ls /run/log/journal/ ls -d /var/log/journal; echo "exit=$?" journalctl --disk-usage
socket dev-log stdout streams 这类文件;第二条是 <machine-id> 目录,里面躺着一堆 *.journal;第三条报 No such file or directory(exit=2);--disk-usage 打印 Archived and active journals take up 3.3M 之类的数字(本机实测约 3.3M)。/run/systemd/journal/ 是 socket 目录(进程把日志交给 journald 的通道),不是数据;数据在 /run/log/journal/<machine-id>/;/var/log/journal/ 是持久化目录,不存在就说明现在没开持久。/run 是 tmpfs,内存里的东西 ⇒ 默认重启即丢。所以「日志重启后没了」不是故障,是默认行为。journalctl -p 3 --no-pager | wc -l journalctl -p 2 --no-pager | wc -l journalctl -p err --no-pager | wc -l journalctl -p crit -n 5 --no-pager
-p 3 与 -p err 条数完全相同(本机 13 行),-p 2 一定更少(7 行);-p crit -n 5 只出 5 行。0 emerg / 1 alert / 2 crit / 3 err / 4 warning / 5 notice / 6 info / 7 debug。-p err 的含义是「err 及更严重」,所以它包含 emerg / alert / crit / err 四档。数字和字母是同一个东西的两种写法,
-p 3 == -p err。logger -p debug -t labdebug "debug line only" journalctl -t labdebug --no-pager grep labdebug /var/log/messages; echo "grep_exit=$?" logger -p info -t labinfo "info line" grep labinfo /var/log/messages | tail -1 grep -n "messages" /etc/rsyslog.conf
journalctl -t labdebug 能看到 debug line only;grep labdebug /var/log/messages 什么都没有、exit=1;换成 -p info 立刻就在 messages 里 grep 得到;rsyslog.conf 里能看到 *.info;mail.none;authpriv.none;cron.none /var/log/messages。systemd-journald → imjournal 模块 → rsyslog → /var/log/messages / secure / cron。rsyslog 是 journald 的下游消费者,不是平行的一套。*.info 是「info 及以上」⇒ debug 在这一层被丢掉,只留在 journal 里。所以「两边都有」是常态,想找只进 journal 的证据必须挑它过滤掉的级别。logger -t labtest "hello" 演示,默认 priority 是 info,两边都有,等于什么都没证明。给演示命令前先验证它能证明标题里的结论。journalctl -o json-pretty -n 1 --no-pager journalctl -F _SYSTEMD_UNIT | head journalctl _SYSTEMD_UNIT=crond.service --no-pager | tail -5 journalctl -u crond --since "1 hour ago" --no-pager
_PID _UID _COMM PRIORITY _SYSTEMD_UNIT _BOOT_ID;-F 列出该字段出现过的所有取值;按字段过滤的结果与 -u crond 一致。grep 只能搜文本,没法区分「谁产的、哪个 boot、什么优先级」;按字段过滤能在源头筛,不会把别的 unit 里同样字样的行一起捞进来。-u(哪个 unit)、-p(多严重)、--since/--until/-b(哪段时间 / 本次启动)。三个可以叠用。ls -d /var/log/journal; echo "before_exit=$?" journalctl --flush; echo "flush_exit=$?" ls -d /var/log/journal; echo "after_exit=$?"
before_exit=2(目录不存在);--flush 一行输出都没有、flush_exit=0;after_exit 仍然是 2 —— 目录压根没被创建。--flush 只把 /run/log/journal 里已有的记录刷进 /var/log/journal。目标目录不存在时它无事可做,但「无事可做」不算错误,于是静默返回 0。ls /var/log/journal/*/ 里有没有 *.journal 文件、--disk-usage 的数字有没有变大。mkdir -p /var/log/journal
ls -ld /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
ls -ld /var/log/journal
systemctl restart systemd-journald
journalctl -u systemd-journald -b --no-pager | grep "Journal ("
ls -ld /var/log/journal/*/
ls -l /var/log/journal/*/*.journal
journalctl --disk-usage
① 第一次
ls -ld:drwxr-xr-x. root root —— 755,没有 s 位、没有 +
② tmpfiles 之后:
drwxr-sr-x+ root systemd-journal —— 2755 + ACL(+ 就是 ACL 的标记)
③ 重启后
grep 那行:从 Runtime Journal (/run/log/journal/...) 变成 System Journal (/var/log/journal/<machine-id>) is 8.0M, max 1.0G
④
ls 出现 <machine-id>/system.journal,大小 8388608(8 MiB)
⑤
--disk-usage 从 1.6M 变 8.0MStorage= 默认是 auto(配置里通常是注释掉的),语义只有一句 —— /var/log/journal 存在才持久化。所以要动手的是目录,不是配置项。systemd-tmpfiles --create 这一步不能省。本机实测:把 natasha 加进 systemd-journal 组后,2755 + ACL 的目录下她能读(journalctl 退出 0);而 Storage=persistent 自动建出来的 755 root:root 目录下,她照样读不到(No journal files were opened due to insufficient permissions,退出 1)。--flush 才出现」——不准确。本机重测:restart 一返回,<machine-id>/ 和 8MB 的 system.journal 当场就在。--flush 干的是「把 /run 里已有记录搬过去」,而 restart 启动时自己已经刷过一遍了,所以再敲 --flush 一行输出都没有。判据用
grep "Journal (" 那行 Runtime→System 的切换,别用「目录里有没有文件」。cp /etc/systemd/journald.conf /tmp/jc.bak mv /var/log/journal /tmp/jw sed -i '/^Storage=/d' /etc/systemd/journald.conf sed -i 's/^\[Journal\]/[Journal]\nStorage=persistent/' /etc/systemd/journald.conf systemctl restart systemd-journald ls -ld /var/log/journal /var/log/journal/*/ ls -l /var/log/journal/*/*.journal systemd-analyze cat-config systemd/journald.conf | tail -6 # 还原 mv /var/log/journal /tmp/jauto; mv /tmp/jw /var/log/journal cp /tmp/jc.bak /etc/systemd/journald.conf; systemctl restart systemd-journald rm -rf /tmp/jauto /tmp/jc.bak
/var/log/journal 和 <machine-id>/ 自动出现(你根本没 mkdir),但权限是 drwxr-xr-x. root root、文件 -rw-r-----. root root —— 没有 s 位、没有 +。cat-config 尾部能看到 Storage=persistent。·
Storage=persistent:journald 自己 mkdir,来不及跑 tmpfiles ⇒ 只有 root 能读·
mkdir + systemd-tmpfiles --create:目录是 2755 root:systemd-journal + ACL ⇒ 组员也能读考试两种都算对;生产上建议后者(或补一次
systemd-tmpfiles --create)。systemd-analyze cat-config 才是「配置到底生效了没」的正确验收:它把主文件 + 所有 .conf.d/*.conf 合并后打印出来。cat /etc/systemd/journald.conf 只能看到一份,看不到 drop-in。journalctl --rotate ls -l /var/log/journal/*/*.journal du -h /var/log/journal/*/*.journal journalctl --disk-usage journalctl --vacuum-size=1M journalctl --disk-usage journalctl --vacuum-time=1s journalctl --vacuum-files=1
·
--rotate 后多出一个 system@<machine-id>-0000…-00065f81….journal
·
ls -l:归档文件 8388608,活动文件也是 8388608 —— 两个都显示 8MB
·
du -h:归档 3.7M,活动 8.0M —— 同一个文件,两个命令两个答案
·
--disk-usage:11.6M(8.0 + 3.6)
·
--vacuum-size=1M:Deleted archived journal … (3.6M) + freed 3.6M,之后 --disk-usage = 8.0M
·
--vacuum-time=1s:freed 0B(没有归档文件可删了)ls 和 du 不一样:journal 文件是按固定大小预分配的(这里是 8 MiB 一个槽),文件名里的 8388608 是表观大小;归档时只保留实际写过的块,du 看到的是实际占用(3.7M)。--disk-usage 统计的是实际块。--vacuum-* 永远删不到 0:它只删归档文件(system@…),正在写的 system.journal 一个都不碰。所以活动文件有多大,--disk-usage 的下限就是多大。别拿「删完还是 8.0M」判断 vacuum 失败。--vacuum-size= 按总量砍、--vacuum-time= 按时间砍、--vacuum-files= 按文件个数砍。都只作用于归档文件,都需要 --rotate 先把活动文件切出去。本机 --vacuum-files=1 时只剩一个活动文件,输出 freed 0B。mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=20M\n' > /etc/systemd/journald.conf.d/99-lab.conf
systemctl restart systemd-journald
journalctl -u systemd-journald -b --no-pager | grep "Journal ("
systemd-analyze cat-config systemd/journald.conf | tail -6
journalctl --rotate; ls -l /var/log/journal/*/system.journal
# 还原
rm /etc/systemd/journald.conf.d/99-lab.conf
systemctl restart systemd-journald
· 默认:
System Journal (…) is 6.2M, max 1.0G, 1.0G free.
· 加 drop-in 后:
is 6.2M, max 20.0M, 13.7M free.
·
cat-config 尾部多出一段 # /etc/systemd/journald.conf.d/99-lab.conf + SystemMaxUse=20M
· rotate 后新的活动文件变成 2621440(2.5 MiB)
SystemMaxFileSize 没写时默认 = SystemMaxUse / 8。20M ÷ 8 = 2.5M,实测 2621440 正好对上。调小 SystemMaxUse 会连带把单个文件切小。SystemMaxUse= 限总量(按空间淘汰),MaxRetentionSec= 限保留时长(按时间淘汰,如 1week)。--vacuum-* 是立即执行一次,写进配置才是以后一直执行。/etc/systemd/journald.conf:drop-in 好还原、升级不冲突、一个文件一件事。但验收必须用 systemd-analyze cat-config —— 只看主文件会以为「我没改过」,实际被 drop-in 改了。rm 是 alias rm='rm -i'(写在 /root/.bashrc:20)。敲 rm /etc/systemd/journald.conf.d/99-lab.conf 会停在 rm: remove regular file …?,你紧接着敲的那条命令会被当成它的回答 —— 不是 y 就等于「不删」,而且那条命令根本不会执行。实测结果:drop-in 留了下来、journald 的 MainPID 还是上一次重启的,max 一直显示 20.0M,看起来像「配置改不回去」,其实是既没删也没重启。绕法:
\rm 文件(反斜杠跳过别名)或 rm -f 文件;删完 ls 确认空目录,再 restart。echo:echo '[Journal]\nSystemMaxUse=20M' 会原样写进 \n(bash 的 echo 默认不解释转义),cat 出来是一行带反斜杠的乱内容。用 printf(本步给的写法)或 echo -e。4 · 附录:命令速查 按「我想干什么」查
| 我想… | 命令 | 注意 |
|---|---|---|
| 看它现在跑不跑 | systemctl is-active X | 只看进程,不看自启 |
| 看它开机会不会自己起 | systemctl is-enabled X | 只看链接,不看进程 |
| 设成开机自启并立刻启动 | systemctl enable --now X | 等于 enable + start |
| 取消自启并立刻停止 | systemctl disable --now X | 等于 disable + stop |
| 彻底封死,连手动启都不行 | systemctl mask X | 先 unmask 才能 enable |
| 看这个服务最近干了什么 | systemctl status X | 尾部日志是摘要,完整要用 journalctl |
| 看 unit 文件全文 | systemctl cat X | 会显示实际生效的路径 |
| 看它挂在哪个 target 下 | systemctl show -p WantedBy X | 决定 enable 往哪建链接 |
| 重启进程(会断连接) | systemctl restart X | PID 一定变 |
| 只重读配置(不断连接) | systemctl reload X | PID 不变;先查 CanReload |
| 这个服务支不支持 reload | systemctl show -p CanReload X | no 时硬 reload 退出码 3 |
| 它为什么不能 enable | systemctl is-enabled X | 返回 static 就是没有 [Install] 段 |
| 看当前默认启动目标 | systemctl get-default | 读的是 default.target 软链 |
| 改下次开机的目标 | systemctl set-default X.target | 只改链接,不影响当前 |
| 立刻切到某个目标 | systemctl isolate X.target | 只改现在,不改默认;rescue 会断网 |
| 看某个目标包含哪些东西 | systemctl list-dependencies X.target | 看的是依赖树,不是配置 |
| 查最近日志 | journalctl -n 50 --no-pager | 不加 --no-pager 会进 less |
| 只看某个服务的 | journalctl -u X | 等价于按 _SYSTEMD_UNIT 过滤 |
| 只看错误及以上 | journalctl -p err | 是「≤ err」,含 crit/alert/emerg |
| 只看本次开机 | journalctl -b | -b -1 是上一次 |
| 看日志占多大 | journalctl --disk-usage | 统计实际块,不是 ls 的表观大小 |
| 让日志重启后还在 | mkdir /var/log/journal + systemd-tmpfiles --create --prefix /var/log/journal + restart journald | tmpfiles 那步决定组员能不能读 |
| 立即清理日志 | journalctl --rotate 再 --vacuum-size=100M | 只删归档,活动文件不碰 |
| 永久限制日志体积 | SystemMaxUse= 写进 drop-in 再 restart | 用 systemd-analyze cat-config 验收 |
5 · 自测 先自己答,再展开
1. disable 之后服务还在跑,正常吗?
正常。disable 只删 wants/ 里的软链接,影响的是「下次开机」,不影响当前进程。要停得另外 stop。
2. stop 之后 is-enabled 为什么还是 enabled?
因为 stop 走的是进程控制路径,enable 走的是链接路径,两者根本不通信。
3. mask 之后为什么连 enable 都失败?
enable 需要读 unit 的 [Install] 段才知道往哪建链接;mask 让该路径指向 /dev/null,读不到内容,于是无从下手。必须 unmask 先。
4. unmask 之后为什么直接回到 enabled 而不是 disabled?
因为 wants/ 里的自启链接在 mask 期间从未被删除。unmask 只删掉 /etc/systemd/system/X -> /dev/null 这个遮盖。
5. list-unit-files --state=running 为什么返回 0?
running 是 sub-state,list-unit-files 不接受;它静默返回 0 unit files listed. 并给退出码 1。running 只能配 list-units。
6. static 的服务能 enable 吗?
不能。static 表示 unit 文件里没有 [Install] 段,enable 没有可写的目标,会报 The unit files have no installation config。本机例子:dnf-makecache.service。但注意它的退出码是 0 —— 不能靠 $? 判断。
7. restart 和 reload 怎么选?
restart 杀进程重开,PID 变,连接全断;reload 只给现有进程发信号(如 HUP),PID 不变,连接不断。改端口/换 socket 必须 restart;只改配置内容优先 reload,但要先用 show -p CanReload 确认支持。
8. enable --now 等于什么?服务已经在跑时它会不会重启它?
等于 enable + start。已经在跑时 start 是空操作,不会重启,PID 不变。要换进程得用 restart 或 try-restart。
9. --state=enable 和 --state=enabled 差在哪?
前者是拼错的非法值,但 systemctl 不校验,直接过滤出 0 个、stderr 全空、退出码 1;后者才是合法值(23 个)。--state 只是过滤器,不是校验器。
10. default.target 是什么?get-default 看的是什么?
它不是服务,是一条软链接(/etc/systemd/system/default.target),指向真正的 target(本机是 multi-user.target)。get-default 就是读这条链接,所以它和 ls -l 看的是同一个对象。
11. graphical.target 和 multi-user.target 谁包含谁?
graphical 包含 multi-user(依赖树里 multi-user 是它的子项),只多了一个 display-manager.service。所以设成 graphical 不会丢服务。
12. isolate 之后 get-default 会变吗?
不会。isolate 换的是当前运行目标,set-default 改的是下次开机的链接,两条路互不通信。和 stop / enable 的关系完全同构。
13. 什么时候该用 rescue / emergency?
rescue:挂载本地文件系统、拉起 sysinit.target + rescue.service(单用户、要 root 密码),修 fstab、改服务配置。emergency:只起 emergency.service,连本地文件系统都只读挂 /,用于根分区都起不来的情况。两者都没有网络和 sshd,只能走控制台。
14. restart systemd-journald 之后 /var/log/journal 里就一定有 *.journal 吗?
有。<machine-id>/system.journal 在 journald 启动时就建好并预分配(本机 8 MiB),不需要额外 --flush。判据看 journalctl -u systemd-journald -b | grep "Journal (" 那行是 Runtime 还是 System。
15. --disk-usage 从 1.6M 变 8.0M,是日志条目变多了吗?
不是。新建的 system.journal 预分配 8 MiB 固定槽位,而里面当时只有 17 行记录。变的是预分配的空间,不是内容。ls -l 看表观大小、du 看实际块,--disk-usage 跟的是实际块。
16. --vacuum-size=1M 打印 freed 3.6M,但 --disk-usage 还有 8.0M,失败了吗?
没失败。它删的是归档文件(system@…,实际占 3.6M),活动文件 system.journal 本身占 8.0M 且永远不会被 vacuum 删掉。总量从 11.6M 降到 8.0M,正好少 3.6M。
17. Storage=persistent 和 mkdir + systemd-tmpfiles 差在哪?
都能持久化,差在权限:前者 journald 自己建目录 ⇒ 755 root:root,非 root 读不到;后者靠 tmpfiles 修成 2755 root:systemd-journal + ACL ⇒ systemd-journal 组成员能读(本机用 natasha 实测:前者 exit=1,后者 exit=0)。
18. 日志状态里那个 max 1.0G 是谁定的?
不是配置里写死的——journald.conf 里 #SystemMaxUse= 是注释掉的。它是默认算法:取 /var/log 所在文件系统大小的 10%。本机根分区 10875830272 字节 × 10% ≈ 1.01 GiB,所以显示 1.0G。SystemMaxFileSize 默认再取它的 1/8。改 SystemMaxUse 会连带改单文件大小(20M ⇒ 2.5M,实测 2621440)。
19. 敲 rm 文件 时它问了一句 remove regular file …?,文件删了吗?
看你回了什么。root 的 rm 是 alias rm='rm -i'(/root/.bashrc),回车不是 y 就不删;更坑的是你下一条命令会被它当成回答,于是那条命令也不会执行。绕法:\rm 文件 或 rm -f 文件。