显然,人们试图让通过ssh
运行启动的进程保持运行,即使连接断开也是如此,请参见https://duckduckgo.com/?q=ssh+process+still+active+after+connection+close
所以我一直相信进程通常会随着连接而终止——直到今天。
随机查看我机器上grep
的输出,我发现了我生成的进程ps
周以前使用过ssh
,我想知道他们是怎么到那里的,现在我不知道如何不是保持进程活跃(参见https://xkcd.com/2797)。
启动长时间运行的进程,例如htop
,sleep
或者dmesg
以普通用户身份(但任何其他用户也可以)像这样
ssh localhost sleep 123
存活直到我手动杀死它们。
pstree -spc $(pgrep -f "sleep 123")
当连接仍然处于活动状态时
systemd(1)───sshd(1299)───sshd(1887616)───sshd(1887654)───sleep(1887655)
但在我终止 ssh 会话(使用 CTRL-C)后,同样的命令给了我
systemd(1)───sleep(1887655)
所以对我来说,它看起来像是sshd
意识到连接已断开,但它没有终止相关进程,而是将其交给了systemd
。
即使其他人似乎都试图实现相反的目标,我如何确保终止连接ssh
也会终止/杀死生成的进程?
注意:ssh localhost -t sleep 123
以某种方式解决了我的问题,但在我看来,因为它改变了 stdin/stdout 的处理方式。我可以告诉ssh
在连接关闭后不要移交进程吗?
答案1
所以对我来说,sshd 似乎意识到连接已断开,但它没有终止相关进程,而是将其交给 systemd。
sshd 永远不会真正终止进程。它所做的只是在您断开连接时退出,剩余的子进程会自动获得 PID 1 作为其新的父进程;您期望的自动退出是由其他东西完成的。
(此外,这是不是通过 sshd 进行明确移交 - 只要一个进程不再有父进程,就会发生这种情况,这是自 UNIX 早期以来的标准行为。)
如果你运行交互的SSH 连接(与 tty 关联的连接)然后让 sshd exit 将隐式“挂断”tty 设备,这会导致操作系统自动向所有将其作为控制 tty 的进程发送 SIGHUP。大多数程序将此信号视为它们应该退出的指示。因此,这个流行nohup
工具的名字是——它只需忽略 SIGHUP 即可让程序继续运行。(尽管“守护进程”经常将其误用为“重新加载”信号,因此不会退出。)
上述情况发生在以下两种情况下:a)未指定任何命令并进入交互式 shell;或者 b)指定命令和选项-t
。
但是,ssh
使用直接命令(而不是-t
)运行时会禁用 tty 分配,并用管道替换 stdin/stdout - 比较ssh myhost tty
-ssh -t myhost tty
并且管道不会执行“立即 SIGHUP”操作,这意味着没有任何东西可以立即发出退出信号,因此它们继续运行,操作系统只是将它们重新置于 PID 1 之下。它们最终可能会退出或在稍后被杀死当他们尝试将某些内容写入标准输出时并收到 SIGPIPE(由于 stdout 管道的另一端已关闭),但它们也可能不会。
(在此模式下使用管道是因为它们是“8 位干净的”,即,任何从 0 到 255 的字节值都将不加改变地通过,而 tty 设备通常执行额外的处理,这对于交互式使用很有用,但会破坏二进制数据。)
sshd 可以使用一些机制来使子进程死亡(PDEATHSIG),但据我所知,它从未使用过这些机制。
只有 systemd 才真正添加了一个杀死剩余进程的新机制。如果你KillUserSomething=
在 /etc/systemd/logind.conf 中启用了该机制,它将导致 systemd 在会话结束时(或者当用户不会话了;我记不清了)。有些发行版默认启用此功能,有些则不启用。
systemd 机制通过 PAM“会话”机制工作,当 sshd 在退出之前调用“关闭会话”(pam_systemd 也有办法检测进程何时死亡)并终止控制群组由 systemd-logind 管理。