【问题标题】:Spawning a process with {create_group=True} / set_pgid hangs when starting Docker启动 Docker 时使用 {create_group=True} / set_pgid 生成进程挂起
【发布时间】:2020-09-03 10:52:09
【问题描述】:

给定一个 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_group=True 时,进程挂起。 create_group 的作用是用0 in the childpid in the parent 调用set_pgid。为什么这种变化会导致挂起?这是 Docker 中的错误吗? System.Process 中的错误?还是不幸但必要的互动?

【问题讨论】:

  • 它似乎与-i : Keep STDIN open even if not attached 相关:仅使用-t 而不是-it,挂起就消失了。

标签: docker haskell process process-group


【解决方案1】:

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

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    if(setpgid(0, 0)) {
        perror("setpgid");
        return 1;
    }
    execlp("docker", "docker", "run", "-it", "alpine", "echo", "hello", (char*)NULL);
    perror("execlp");
    return 1;
}

如果你编译它并直接从你的交互式 shell 运行 ./a.out,它会像你期望的那样打印“hello”。这并不奇怪,因为 shell 已经将它放在自己的进程组中,所以它的setpgid 是一个空操作。如果您使用派生子运行它的中间程序运行它(sh -c ./a.out\time ./a.out - 注意反斜杠、strace ./a.out 等),那么setpgid 会将其放入一个新的进程组中,它会像在 Haskell 中一样挂起。

挂起的原因在"Job Control Signals" in the glibc manual中有解释:

宏:int SIGTTIN

进程在作为后台作业运行时无法从用户终端读取。当后台作业中的任何进程尝试从终端读取时,作业中的所有进程都会收到SIGTTIN 信号。此信号的默认操作是停止进程。有关它如何与终端驱动程序交互的更多信息,请参阅Access to the Terminal

宏:int SIGTTOU

这类似于SIGTTIN,但在后台作业中的进程尝试写入终端或设置其模式时生成。同样,默认操作是停止进程。 SIGTTOU 仅在设置了TOSTOP 输出模式时才会生成尝试写入终端;见Output Modes

当你docker run -it 时,Docker 将尝试从标准输入读取,即使容器内的命令没有。由于您刚刚创建了一个新的进程组,并且您没有将其设置为在前台,因此它被视为后台作业。因此,Docker 被 SIGTTIN 停止,这导致它看起来挂起。

以下是解决此问题的选项列表:

  1. 将进程的标准输入重定向到 TTY 以外的其他地方
  2. 使用signalsigaction 使进程忽略SIGTTIN 信号
  3. 使用sigprocmask 阻止进程接收SIGTTIN 信号
  4. 调用@987654327@(0, getpid()) 使您的新进程组成为前台进程组(注意:这是最复杂的,因为它本身会导致SIGTTOU,所以无论如何您都必须至少暂时忽略该信号)

选项 2 和 3 也仅在程序实际上不需要标准输入时才有效,Docker 就是这种情况。当SIGTTIN 没有停止该过程时,从标准输入读取仍然会失败并显示EIO,因此如果您确实要读取数据,那么您需要使用选项4(并记住一旦孩子将其设置回来退出)。

如果您设置了 TOSTOP(这不是默认设置),那么您必须对 SIGTTOU 或标准输出和标准错误重复修复(选项 4 除外,它不需要完全重复)。

【讨论】:

  • 谢谢!出于我的目的,关闭标准输入会使问题消失。似乎存在一个问题,即新进程组的标准输入在读取时会挂起,这是一个问题。
  • @NeilMitchell docker run -it 即使在不需要的情况下也可以从标准输入读取,这是我错过的部分。有了那条信息,我现在明白了发生了什么。我用更长的解释更新了答案,并提供了一些替代解决方案,如果你最终需要这样做,它们仍然可以工作,同时仍然让你使用标准输入。
  • 这真的很有帮助!这确实解释了一切。
猜你喜欢
  • 2011-08-11
  • 2019-03-22
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
  • 2018-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多