启动Docker时,生成具有{create_group=True}/set_pgid的进程将挂起

启动Docker时,生成具有{create_group=True}/set_pgid的进程将挂起,docker,haskell,process,process-group,Docker,Haskell,Process,Process Group,给定一个Linux系统,在Haskell GHCi 8.8.3中,我可以使用以下命令运行Docker命令: System.Process> withCreateProcess (shell "docker run -it alpine sh -c \"echo hello\""){create_group=False} $ \_ _ _ pid -> waitForProcess pid hello ExitSuccess 但是,当我切换到create\u group=True时,

给定一个Linux系统,在Haskell GHCi 8.8.3中,我可以使用以下命令运行Docker命令:

System.Process> withCreateProcess (shell "docker run -it alpine sh -c \"echo hello\""){create_group=False} $ \_ _ _ pid -> waitForProcess pid
hello
ExitSuccess

但是,当我切换到
create\u group=True
时,进程挂起。
create_group
的作用是使用和调用
set_pgid
。为什么这种变化会导致挂起?这是Docker的一个bug吗?系统进程中的错误?还是一个不幸但必要的交互?

这不是Haskell中的错误或Docker中的错误,而是流程组的工作方式。考虑这个C程序:

#包括
#包括
#包括
内部主(空){
if(setpgid(0,0)){
perror(“setpgid”);
返回1;
}
execlp(“docker”、“docker”、“run”、“-it”、“alpine”、“echo”、“hello”、(char*)NULL);
perror(“execlp”);
返回1;
}
如果您编译它并直接从交互式shell运行
/a.out
,它将按预期打印“hello”。这并不奇怪,因为shell已经将它放在自己的进程组中,所以它的
setpgid
是不可操作的。如果您使用一个中间程序运行它,该程序会派生一个子程序来运行它(
sh-c./a.out
\time./a.out
-请注意反斜杠,
strace./a.out
,等等),然后,
setpgid
将把它放在一个新的进程组中,它将像在Haskell中一样挂起

挂起的原因解释如下:

宏:int-sigtin

进程作为后台作业运行时,无法从用户终端读取数据。当后台作业中的任何进程尝试从终端读取时,该作业中的所有进程都会发送一个
SIGTTIN
信号。此信号的默认操作是停止进程。有关如何与终端驱动程序交互的更多信息,请参阅

宏:int sigttoo

这类似于
sigtin
,但在后台作业中的进程尝试写入终端或设置其模式时生成。同样,默认操作是停止该过程<如果设置了
TOSTOP
输出模式,则仅为尝试写入终端而生成code>sigttoo;看

当您
docker运行-it
something时,docker将尝试从stdin读取数据,即使容器中的命令没有读取数据。由于您刚刚创建了一个新的流程组,并且没有将其设置为前台,因此它将被视为后台作业。因此,Docker将被
SIGTTIN
停止,这导致它看起来挂起

以下是修复此问题的选项列表:

  • 将流程的标准输入重定向到TTY之外的其他位置
  • 使用或使进程忽略
    sigtin
    信号
  • 用于阻止进程接收信号
  • 调用
    (0,getpid())
    将新进程组设置为前台进程组(注意:这是最复杂的,因为它本身会导致
    sigttoo
    ,因此您必须至少暂时忽略该信号)
  • 选项2和3也只有在程序实际上不需要stdin时才起作用,Docker就是这样。当
    SIGTTIN
    没有停止进程时,从stdin读取仍然会失败,并带有
    EIO
    ,因此如果确实有数据需要读取,则需要使用选项4(记住在子项退出后将其设置回原位)


    如果设置了
    TOSTOP
    (这不是默认设置),则必须对
    sigttoo
    或标准输出和标准错误重复修复(选项4除外,该选项根本不需要重复).

    它似乎与
    -i:即使未连接,也保持STDIN打开
    :只需
    -t
    而不是
    -It
    ,挂起就会消失。谢谢!就我而言,关闭stdin可以解决问题。似乎有一个问题是,一个新的进程组在读取时得到一个挂起的stdin,这是一个问题。@NeilMitchell事实上,
    docker run-it
    从stdin读取,即使它不需要,这也是我丢失的部分。有了这条信息,我现在明白了发生的一切。我用更长的解释更新了答案,并提供了一些替代解决方案,如果您最终需要使用stdin,这些解决方案仍然可以使用stdin。这真的很有帮助!这就解释了一切。