bash - 为什么 ssh 在没有 -t 的情况下等待我的子 shell,然后用 -t 杀死它们?

标签 bash ssh tty subshell

我有一个 bash 脚本 start.sh,如下所示:

for thing in foo bar; do
    {
        background_processor $thing
        cleanup_on_exit $thing
    } &
done

这就是我想要的:我运行 start.sh,它以代码 0 退出,并且两个子 shell 在后台运行。每个子外壳运行 background_processor,当它退出时,它运行 cleanup_on_exit。即使我退出最初运行 start.sh 的终端(即使那是一个 ssh 连接),这仍然有效。

然后我试了一下:

ssh user@host "start.sh"

这是有效的,除了在 start.sh 退出之后,ssh 显然也在等待子 shell 退出。我真的不明白为什么。一旦 start.sh 退出,子 shell 就会成为 pid 1 的子 shell,它们甚至没有分配 tty...所以我不明白它们如何仍然与我的 ssh 连接相关联。

我后来试过这个:

ssh -t user@host "start.sh"

现在进程有一个分配的伪终端。现在,我发现 ssh 会在 start.sh 退出时立即退出,但它也会终止子进程。

我猜想在后一种情况下子进程被发送了 SIGHUP,所以我这样做了:

ssh -t user@host "nohup start.sh"

这确实有效!所以,我有一个解决我的实际问题的方法,但我想在这里掌握 SIGHUP/tty 内容的微妙之处。

总而言之,我的问题是:

  1. 为什么即使在 start.sh 退出后 ssh(不带 -t)仍在等待子进程,即使它们的父 pid 为 1?
  2. 为什么 ssh(使用 -t)会终止子进程,显然是通过 SIGHUP,即使当我从终端运行它们并从该终端注销时并没有发生这种情况?

最佳答案

我想我现在可以解释了!我必须通过阅读 The TTY Demystified 了解一些 session 和进程组是什么。 .

  1. Why does ssh (without -t) wait for the child processes even after start.sh exits, even though they have parent pid 1?

因为没有 tty,ssh 通过管道(然后由子进程继承)连接到 shell 进程的 stdin/stdout/stderr,而我使用的 OpenSSH 版本 (OpenSSH_4.3p2) 等待这些套接字退出前关闭。某些较早版本的 OpenSSH 不以这种方式运行。对此有一个很好的解释,有理由,here .

相反,当使用交互式登录(或 ssh -t)时,ssh 和进程正在使用 TTY,因此没有管道等待。

我可以通过重定向流来恢复我想要的行为。此变体立即返回:ssh user@host "start.sh < /dev/null > /dev/null 2>&1"

  1. Why does ssh (with -t) kill the child processes, apparently with a SIGHUP, even though that does not happen when I run them from a terminal and log out of that terminal?

因为 bash 以非交互模式启动,这意味着默认情况下作业控制是禁用的,因此子进程与父 bash 进程(即 session 领导者)位于同一进程组中。当父 bash 进程退出时,内核将 SIGHUP 发送到其进程组(在前台),如 setpgid(2) 中所述。 :

If a session has a controlling terminal, ... [and] the session leader exits, the SIGHUP signal will be sent to each process in the foreground process group of the controlling terminal.

相反,当使用交互式登录时,bash 处于交互模式,这意味着默认情况下启用作业控制,因此子进程进入一个单独的进程组并且在我退出时永远不会收到 SIGHUP。

我可以使用 set -m 恢复我想要的行为在 bash 中启用作业控制。如果我添加 set -mstart.sh ,当 ssh 退出时, children 不再被杀死。

谜团解开了:)

关于bash - 为什么 ssh 在没有 -t 的情况下等待我的子 shell,然后用 -t 杀死它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14679178/

相关文章:

linux - 当函数返回但函数中的命令在后台运行时会导致什么行为?

go - Go : unable to authenticate, 中的 SSH 尝试了方法 [无],没有支持的方法

cocoa - Cocoa Emacs 中的选项键不输入重音字符

linux - 从一个终端向另一个终端写入命令

c# - 如何连接需要SSH的远程Mysql服务器?

bash - 如何在 linux 中通过 init 杀死重生的进程

bash - 使用 shell 变量传递选项时,autotools 配置错误

bash - 当 POSIX 规范说有必要避免歧义时,它是什么意思?

linux - crontab 中的脚本仅在等于或超过某个值时才执行

git push 返回 "fatal: protocol error: bad line length character"