Bash 为什么ssh在没有-t的情况下等待我的子shell,然后用-t杀死它们?
我有一个bash脚本start.sh,如下所示:Bash 为什么ssh在没有-t的情况下等待我的子shell,然后用-t杀死它们?,bash,ssh,tty,subshell,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在后台运行。每个子shell运行background\u处理器,当处理器退出时,它会在退出时运行cleanup\u。即使我退出最初运行start.
for thing in foo bar; do
{
background_processor $thing
cleanup_on_exit $thing
} &
done
这就是我想要的:我运行start.sh,它以代码0退出,两个子shell在后台运行。每个子shell运行background\u处理器
,当处理器退出时,它会在退出时运行cleanup\u。即使我退出最初运行start.sh的终端(即使这是一个ssh连接),这也可以工作
然后我试了一下:
ssh user@host "start.sh"
ssh -t user@host "start.sh"
这是可行的,除了退出start.sh
之后,ssh显然也在等待子shell退出。我真的不明白为什么。一旦退出start.sh
,子shell将成为pid 1的子shell,并且它们甚至没有分配tty。。。因此,我无法理解它们如何仍然与我的ssh连接相关联
我后来试过:
ssh user@host "start.sh"
ssh -t user@host "start.sh"
现在,进程有一个指定的伪tty。现在,我发现只要start.sh
退出,ssh就会退出,但它也会杀死子进程
我猜在后一种情况下,子进程正在被发送,所以我这样做:
ssh -t user@host "nohup start.sh"
这确实有效!所以,我有一个解决我实际问题的方法,但我想在这里抓住SIGHUP/tty的微妙之处
总之,我的问题是:
为什么ssh(不带-t)即使在start.sh
退出后也要等待子进程,即使它们有父进程pid 1
为什么ssh(带-t)会杀死子进程,显然是通过SIGHUP,即使当我从终端运行它们并从该终端注销时不会发生这种情况
我怀疑(但我假设)当没有tty时,bash会将SIGHUP传递给您的forked进程,该进程自己处理信号,并悄悄地忽略它并继续绑定SSH会话
但是,由于您和进程之间有一个tty,tty驱动程序正在拦截SIGHUP,意识到它已经失去了用户,并在没有ssh会话作为父会话的情况下自行运行。我想我现在可以解释这一点了!我必须通过阅读了解会话和过程组是什么
为什么ssh(不带-t)即使在start.sh退出之后也要等待子进程,即使它们有父进程pid 1
因为没有tty,ssh通过管道(然后由子进程继承)连接到shell进程的stdin/stdout/stderr,而我使用的OpenSSH版本(OpenSSH_4.3p2)在退出之前等待这些套接字关闭。一些早期版本的OpenSSH没有这样做。对此有一个很好的解释,有理由
相反,当使用交互式登录(或ssh-t
)时,ssh和进程使用TTY,因此没有管道等待
我可以通过重定向流来恢复所需的行为。此变量立即返回:sshuser@host“start.sh/dev/null 2>&1”
为什么ssh(带-t)会杀死子进程,显然是通过SIGHUP,即使当我从终端运行它们并从该终端注销时不会发生这种情况
因为bash是在非交互模式下启动的,这意味着默认情况下禁用作业控制,因此子进程与父bash进程(即会话负责人)位于同一进程组中。当父bash进程退出时,内核向其进程组(位于前台)发送SIGHUP,如setpgid(2)
中所述:
如果会话有一个控制终端。。。[和]会话引导器退出,SIGHUP信号将发送到控制终端前台进程组中的每个进程
相反,当使用交互式登录时,bash处于交互式模式,这意味着默认情况下启用作业控制,因此子进程进入一个单独的进程组,并且在我退出时从未收到SIGHUP
我可以通过使用set-m
在bash中启用作业控制来恢复所需的行为。如果我将set-m
添加到start.sh
,则ssh退出时不再杀死子级
谜团已解开:)用“nohup”预先准备好任何你不想让这件事发生的电话。PS:如果我有时间,我计划阅读和消化,之后也许我能回答上面的问题。。。