自定义服务在 systemd 眼里就是普通 unit:写对文件、reload 加载、enable 建软链接,三件事各管各的。
一、unit 文件先写全:少了 Install 段就谈不上 enable
cat > /etc/systemd/system/demo.service <<'EOF'
[Unit]
Description=Demo custom service
[Service]
Type=oneshot
ExecStart=/bin/echo hello-from-demo
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload # 手工写的文件,reload 后守护进程才认
systemctl start demo; echo "rc=$?" # 实测 0
journalctl -u demo -b --no-pager | tail -3
systemctl show -p ActiveState demo # 实测 ActiveState=inactive
实测日志三行:echo 进程打出 hello-from-demo、Deactivated successfully、Finished demo.service – Demo custom service。oneshot 跑完退回 inactive 是正常态,不是失败。
为什么必须 daemon-reload:Debian 手册在 enable 一节写明,管理员手工往配置目录放软链接或改文件后,必须自己跑 daemon-reload 才会重新加载 [1]。同一节还提醒 cat 读的是磁盘文件,改完没 reload 时可能与守护进程的理解不一致 [1]。WantedBy 这类设置只在处理 enable 时被读一次,正常运行时 systemd 不再看这个段 [4];unit 与 drop-in 的查找顺序是 /etc 覆盖 /run、/run 覆盖 /lib,drop-in 优先于同名 unit 文件、同一目录里多个 drop-in 按文件名字典序叠加 [3]。
二、enable 只建软链接,start 才真的起进程
systemctl enable demo # Created symlink .../multi-user.target.wants/demo.service
systemctl is-enabled demo; echo "rc=$?" # enabled,退出码 0
systemctl is-active demo; echo "rc=$?" # inactive,退出码 3
systemctl start demo # 到这一步才有进程
systemctl disable demo # Removed ".../multi-user.target.wants/demo.service"
systemctl is-enabled demo; echo "rc=$?" # disabled,退出码 1
手册口径很硬:enabling 与 starting 是正交的,enable 只是把 unit 挂到建议的位置,start 才真正 spawn 进程 [2];enable 本身不会顺手启动,想一步到位就写 systemctl enable –now,或者稍后再 start [1]。
oneshot 用 is-active 判断到底跑过没有,会一直拿到 inactive(实测退出码 3),要看 journalctl -u 或 status 的 Active 行。常驻服务才有运行态可查:实测一个 ExecStart 为 sleep 120 的 Type=simple 测试 unit,运行中 show 报 ActiveState=active、is-active 退出码 0,stop 之后回到 inactive、退出码 3 [1]。
三、没有 Install 段的 unit:能 start,不能 enable
systemctl enable demo2; echo "rc=$?" # 只打警告,退出码仍是 0,且没建软链接
systemctl is-enabled demo2; echo "rc=$?" # static,退出码 0
systemctl start demo2; echo "rc=$?" # 退出码 0 —— static 照样能手动起
systemctl is-active demo2; echo "rc=$?" # active,退出码 0
实测警告原文以 The unit files have no installation config 开头,落到 they are not meant to be enabled or disabled using systemctl 结束,既不报错也不建软链接,退出码 0 [1]。手册给这种 unit 的名字是 static,定义就是没被 enable、且 Install 段里没有可 enable 的配置,is-enabled 对它返回 0 [1]。
这里有个常见误传:static 就起不来。实测不成立,static unit 用 systemctl start 手动激活完全正常,start 退出码 0、随后 is-active 为 active。真实边界只有一条,它不能被 enable,所以开机不会自动带上它,要么补上 Install 段再 enable,要么由别的 unit 拉起来。
四、退出码别照抄旧表:本机 systemd 255 的实测值
systemctl is-enabled demo; echo $? # enabled -> 0
systemctl is-enabled demo2; echo $? # static -> 0
systemctl is-enabled demo3; echo $? # 有 Install 段但被 disable -> disabled, 1
systemctl is-active demo; echo $? # 没在跑 -> inactive, 3
systemctl status demo4; echo $? # Unit demo4.service could not be found. -> 4
手册 EXIT STATUS 一节直接说明 systemctl 沿用 LSB 3.0.0 定义的返回码,表里 3 对应 unit is not active、4 对应 no such unit(本机 systemd 255 手册原文)。手册对 is-active 只承诺至少一个是 active 就返回 0、否则非 0 [2],is-enabled 状态表把 disabled、masked 一律写作大于 0 [1]。
结论是:把 is-active 的失败当成 1、把文件不存在和没在跑混成一个码,都会让脚本误判。要分得清就同时读输出文本,或者直接用 systemctl show -p ActiveState demo 取值,实测 active 与 inactive 两种结果都对得上。
五、改配置:手写 drop-in 要 reload,revert 是撤销键
mkdir -p /etc/systemd/system/demo.service.d
printf '[Service]\nEnvironment=DEMO_MODE=dropin\n' > /etc/systemd/system/demo.service.d/override.conf
systemctl daemon-reload
systemctl cat demo | grep override.conf # 输出里出现该文件名注释,说明 drop-in 已拼上
systemctl revert demo # Removed 该 override.conf,连目录一起删
脚本里非交互改配置就照上面三行写。别指望 systemctl edit –stdin:本机 systemd 255 的 systemctl edit 帮助里 grep 不到 stdin,这类写法直接用不了;edit 自己会在编辑器退出后做一次等效 daemon-reload [1]。
回滚用 revert 最省事,它把 drop-in 文件和它所在的目录一并移除。但对本机这种没有包自带版本的手写 unit,revert 退出码 0 却什么都没删,unit 文件原样留着(实测)——手写的东西最后还是得自己 rm 再加 daemon-reload。
六、彻底停掉一个 unit:mask 的边界和手写 unit 的下线顺序
**常见问题**
局限性:本文环境为 WSL2 Ubuntu 24.04.4 LTS、systemd 255(255.4-1ubuntu8.16)、单用户 root;开机自启只验证到 wants 软链接与手册语义,未做真实重启验证;RPM 系发行版与 systemd user 实例未覆盖;systemctl edit 只做了帮助选项与文档核查,交互式编辑流程未逐步实操;退出码读数以本机 systemd 255 为准,更早或更新版本请自行复核。
**实测记录**
参考资料
- [1] systemctl 1 — Debian manpages (systemd)(Debian systemctl(1))
- [2] systemctl(1) — Linux manual page (man7)(man7 systemctl(1))
- [3] systemd.unit(5) — Debian manpages(Debian systemd.unit(5))
- [4] systemd.unit(5) — Linux manual page (man7)(man7 systemd.unit(5))
文中主要命令已在 WSL Ubuntu(24.04)实测,个别条目(未装工具/安装类命令)仅核对官方文档,未实际执行。