![断开 SSH 会话的连接是否会终止你的程序?](https://linux22.com/image/603662/%E6%96%AD%E5%BC%80%20SSH%20%E4%BC%9A%E8%AF%9D%E7%9A%84%E8%BF%9E%E6%8E%A5%E6%98%AF%E5%90%A6%E4%BC%9A%E7%BB%88%E6%AD%A2%E4%BD%A0%E7%9A%84%E7%A8%8B%E5%BA%8F%EF%BC%9F.png)
那么,假设我在启动 SSH 会话后断开连接rsync
,或者cp
执行任何其他可能长时间运行的命令。断开连接后,该命令会继续运行直到完成,还是会直接被终止?
一直想知道这一点。
答案1
2016 年编辑:
本次问答早于systemd v230 失败。从 systemd v230 开始,新的默认设置是终止终止登录会话的所有子会话,无论过去采取了哪些有效的预防措施来防止这种情况发生。可以通过KillUserProcesses=no
在 中设置来更改此行为/etc/systemd/logind.conf
,或者使用 systemd 特定的机制在用户空间启动守护进程来规避此行为。这些机制超出了本问题的讨论范围。
下面的文字描述了在 Linux 存在的时间更长的 UNIX 设计空间中事物传统的运作方式。
它们会被杀死,但不一定立即被杀死。这取决于 SSH 守护进程需要多长时间才能确定您的连接已断开。接下来是更详细的解释,可以帮助您了解它的实际工作原理。
当您登录时,SSH 守护程序会为您分配一个伪终端,并将其附加到用户配置的登录 shell。这称为控制终端。您在此时正常启动的每个程序,无论 shell 深度有多少层,最终都会将其祖先追溯到该 shell。您可以使用命令观察这一点pstree
。
当与您的连接关联的 SSH 守护进程确定您的连接已断开时,它会SIGHUP
向登录 shell 发送挂断信号 ( )。这会通知 shell 您已消失,它应该开始自行清理。此时发生的情况是 shell 特有的(在其文档页面中搜索“HUP”),但在大多数情况下,它会在终止之前开始SIGHUP
向与其关联的正在运行的作业发送信号。这些进程中的每一个,依次在收到该信号后执行它们配置的任何操作。通常这意味着终止。如果这些作业有自己的作业,信号通常也会被传递。
在控制终端挂断后仍能幸存下来的进程要么是脱离终端的进程(您在其中启动的守护进程),要么是使用前缀命令调用的进程nohup
。(即“不要挂断此命令”)守护进程对 HUP 信号的解释不同;因为它们没有控制终端,也不会自动接收 HUP 信号,因此它被重新用作管理员重新加载配置的手动请求。具有讽刺意味的是,这意味着大多数管理员直到很久很久以后才了解此信号对非守护进程的“挂断”用法。这就是您阅读本文的原因!
终端多路复用器是保持 shell 环境在断开连接之间完好无损的常用方法。它们允许您以某种方式与 shell 进程分离,以便稍后重新连接到它们,无论断开连接是意外的还是故意的。tmux
和screen
是更流行的;使用它们的语法超出了您的问题范围,但它们值得研究。
有人要求我详细说明 SSH 守护进程需要多长时间才能确定您的连接已断开。这是特定于每个 SSH 守护进程的行为,但您可以指望当任何一端重置 TCP 连接时,它们都会终止。如果服务器尝试写入套接字并且 TCP 数据包未得到确认,则这种情况会很快发生,如果没有任何东西尝试写入 PTY,则这种情况会很慢。
在这种特定情况下,最有可能触发写入的因素是:
- 尝试在服务器端写入 PTY 的进程(通常是前台进程)。(服务器->客户端)
- 用户尝试在客户端写入 PTY。(客户端->服务器)
- 任何类型的 Keepalive。通常默认情况下,客户端或服务器都不会启用 Keepalive,并且通常有两种类型:应用程序级别和基于 TCP(即
SO_KEEPALIVE
)。Keepalive 相当于服务器或客户端偶尔向另一端发送数据包,即使没有其他理由写入套接字。虽然这通常是为了避开过快超时连接的防火墙,但它还有一个额外的副作用,即当另一端没有更快响应时,发送者会注意到。
TCP 会话的通常规则在此适用:如果客户端和服务器之间的连接中断,但在问题发生期间双方均未尝试发送数据包,则只要双方事后均做出响应并接收到预期的 TCP 序列号,则连接将继续存在。
如果一方认为套接字已死,则通常会立即产生影响:sshd 进程将发送HUP
并自行终止(如前所述),或者客户端将通知用户检测到的问题。值得注意的是,仅仅因为一方认为另一方已死并不意味着另一方已收到通知。连接的孤立端通常会保持打开状态,直到它尝试写入并超时,或者从另一方收到 TCP 重置。(如果当时连接可用)本答案中描述的清理仅在服务器已经注意到了。
答案2
正如其他人提到的那样,一旦您断开与 ssh 的连接,其中运行的任何东西都会消失。
作为@迈克尔·汉普顿其他人提到您可以使用tmux
或之类的工具screen
断开/重新连接到终端而不会丢失其内容(即子进程)。
此外,您可以使用 & 符号将进程放入后台&
,然后使用命令disown
将其与当前 shell 取消关联。
# start a command
% sleep 5000 &
[1] 3820
# check it
% jobs
[1]+ Running sleep 5000 &
# disown everything
% disown -a
# check it again (gone from shell)
% jobs
%
# but it's still running on the system
% ps -eaf|grep "[s]leep"
saml 3820 23791 0 00:16 pts/1 00:00:00 sleep 5000
%
答案3
不,任何仍然附加在终端上、并且没有使用类似方法放置在后台的程序nohup
都会被杀死。
这就是为什么会有像tmux
旧版这样的虚拟终端解决方案screen
,它们会创建即使您断开连接也能继续运行的会话,并且您可以稍后重新连接。