nohup 骗了我:Ubuntu 22.04 的 logind 会在 SSH 退出时杀掉所有后台进程
我需要远程改一台服务器的静态 IP。因为 netplan apply 一执行 SSH 就会断(IP 变了),所以我套了 nohup + & + disown,打算让命令在后台跑完,自己主动 exit。
理论天衣无缝。结果 IP 没变,netplan apply 压根没跑完。
现象
nohup sudo netplan apply >/tmp/netplan.log 2>&1 &
disown
exit
# SSH 重连后——/tmp/netplan.log 空的,IP 没变前台直接跑 sudo netplan apply 立刻生效,换成 nohup 版本就失效。
诊断指纹:
journalctl -t systemd-logind | tail -20
# 输出含:Killing user process ... (PID xxxxx)
# 或:Removing user root runtime directory看到 Killing user process,案子就破了。
根因
Ubuntu 22.04 起,systemd-logind 默认开启 KillUserProcesses=yes。含义:用户最后一个 login session 结束时,logind 会向该用户的所有进程发 SIGTERM,紧接着 SIGKILL。
nohup:屏蔽 SIGHUP,对 SIGTERM/SIGKILL 无效disown:把 job 从 shell 的 jobs 表移除,但不解开 cgroup 归属setsid:在某些 logind 版本下仍被 cgroup 杀掉
logind session 结束时走的是 user.slice cgroup 批量杀,不管你 fork 了几层、disown 了没有——只要进程还挂在那个 user slice 下,一律收走。Ubuntu 22.04 的 /etc/systemd/logind.conf 注释掉了这个选项,但代码里的默认值就是 yes,等于开箱即开。
正解
把命令托管给 systemd 的一次性 transient unit,彻底脱离 user session 的生命周期:
sudo systemd-run --no-block --unit=my-task /path/to/cmd arg1 arg2--no-block:systemd-run 立刻返回,不等 unit 结束--unit=<name>:给 unit 起名,方便事后查状态和日志
查看执行结果:
systemctl status my-task.service
journalctl -u my-task --no-pager远程改 IP 的完整安全操作(含 5 分钟自动回滚保险绳):
# 第一步:安排 300 秒后自动回滚(先跑,确保 systemd 里已登记)
sudo systemd-run --on-active=300 --unit=ip-rollback bash -c "
cp /tmp/netplan.bak /etc/netplan/50-cloud-init.yaml
rm -f /etc/netplan/01-static.yaml
/usr/sbin/netplan apply
"
# 第二步:立即 apply 新 IP(SSH 会断,但 rollback timer 已在 systemd 里)
sudo systemd-run --no-block --unit=netplan-apply-now /usr/sbin/netplan apply从新 IP 重新 SSH 进来,如果新 IP 通了就取消回滚:
sudo systemctl stop ip-rollback.timer ip-rollback.service 2>/dev/null
sudo systemctl reset-failed ip-rollback.{timer,service} 2>/dev/null排查速查
# 确认本机是否受影响
loginctl show-user $USER | grep KillUserProcesses
# KillUserProcesses=yes → 受影响
# 查看 logind 日志
journalctl -t systemd-logind | grep -i kill | tail -20如果实在改不了调用方式,可以编辑 /etc/systemd/logind.conf,添加:
KillUserProcesses=no然后 sudo systemctl restart systemd-logind。强烈不建议在多用户或生产机器上这么做——会让所有用户的后台进程跨 session 无限存活,是资源治理和安全的倒退。
一句话外卖
Ubuntu 22.04+ 上,任何需要在 SSH 退出后继续跑的命令,都应该用
systemd-run --no-block托管,而不是nohup。