gub*_*ins 15 ssh bash tty subshell
我有一个bash脚本start.sh,如下所示:
for thing in foo bar; do
{
background_processor $thing
cleanup_on_exit $thing
} &
done
Run Code Online (Sandbox Code Playgroud)
这就是我想要的:我运行start.sh,它以代码0退出,并且两个子shell在后台运行.每个子shell运行background_processor,当它退出时,它运行cleanup_on_exit.即使我退出我最初运行start.sh的终端(即使这是一个ssh连接),这也有效.
然后我尝试了这个:
ssh user@host "start.sh"
Run Code Online (Sandbox Code Playgroud)
这是有效的,除了start.sh退出后,ssh显然也等待子壳退出.我真的不明白为什么.一旦start.sh退出,子shell变成pid 1的子项,并且它们甚至没有被赋予tty ...所以我无法理解它们如何仍然与我的ssh连接相关联.
我后来试过这个:
ssh -t user@host "start.sh"
Run Code Online (Sandbox Code Playgroud)
现在进程有一个指定的伪tty.现在,我发现ssh一旦start.sh退出就会退出,但它也会杀死子进程.
我猜想在后一种情况下子进程被发送了SIGHUP,所以我这样做了:
ssh -t user@host "nohup start.sh"
Run Code Online (Sandbox Code Playgroud)
这实际上有效!所以,我有一个解决我的实际问题的方法,但我想在这里掌握SIGHUP/tty的微妙之处.
总之,我的问题是:
start.sh退出后也等待子进程,即使它们有父pid 1?gub*_*ins 19
我想我现在可以解释一下!我必须学习一些关于会话和进程组的内容,我通过阅读The TTY Demystified来做.
- 为什么即使在start.sh退出之后ssh(没有-t)等待子进程,即使它们有父pid 1?
因为没有tty,ssh通过管道连接到shell进程的stdin/stdout/stderr(然后由子进程继承),我正在使用的OpenSSH版本(OpenSSH_4.3p2)等待这些套接字关闭之前退出.一些早期版本的OpenSSH没有这样做.这里有一个很好的解释,理由是这里.
相反,当使用交互式登录(或ssh -t)时,ssh和进程正在使用TTY,因此没有管道可以等待.
我可以通过重定向流来恢复我想要的行为.此变体立即返回:ssh user@host "start.sh < /dev/null > /dev/null 2>&1"
- 为什么SSH(与-t)杀子进程,显然与一个SIGHUP,即使当我从一个终端运行它们,然后注销该终端不发生?
因为bash以非交互模式启动,这意味着默认情况下禁用作业控制,因此子进程与父bash进程(会话负责人)位于同一进程组中.当父bash进程退出时,内核将SIGHUP发送到其进程组(位于前台),如下所述setpgid(2):
如果会话具有控制终端,...... [和]会话负责人退出,则SIGHUP信号将被发送到控制终端的前台进程组中的每个进程.
相反,当使用交互式登录时,bash处于交互模式,这意味着默认情况下启用了作业控制,因此子进程进入单独的进程组,并且在退出时从不接收SIGHUP.
我可以通过set -m在bash中启用作业控制来恢复我想要的行为.如果我添加set -m到start.sh,孩子们不再杀死SSH退出时.
神秘的问题:)
| 归档时间: |
|
| 查看次数: |
4732 次 |
| 最近记录: |