gpt4 book ai didi

docker - 启动 Docker 时使用 {create_group=True}/set_pgid 生成进程挂起

转载 作者:行者123 更新时间:2023-12-02 17:58:58 24 4
gpt4 key购买 nike

给定一个 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的效果是打电话 set_pgid 0 in the child , 和 pid in the parent .为什么这种变化会导致挂起?这是 Docker 中的错误吗? System.Process 中的错误?还是不幸但必要的互动?

最佳答案

这不是 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;
}

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

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

Macro: int SIGTTIN

A process cannot read from the user’s terminal while it is running as a background job. When any process in a background job tries to read from the terminal, all of the processes in the job are sent a SIGTTIN signal. The default action for this signal is to stop the process. For more information about how this interacts with the terminal driver, see Access to the Terminal.

Macro: int SIGTTOU

This is similar to SIGTTIN, but is generated when a process in a background job attempts to write to the terminal or set its modes. Again, the default action is to stop the process. SIGTTOU is only generated for an attempt to write to the terminal if the TOSTOP output mode is set; see Output Modes.



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

以下是解决此问题的选项列表:
  • 将进程的标准输入重定向到 TTY 以外的其他地方
  • 使用 signal sigaction 使进程忽略 SIGTTIN信号
  • 使用 sigprocmask 阻止进程接收 SIGTTIN信号
  • 调用 tcsetpgrp(0, getpid())使您的新进程组成为前台进程组(注意:这是最复杂的,因为它本身会导致 SIGTTOU ,因此无论如何您必须至少暂时忽略该信号)

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

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

    关于docker - 启动 Docker 时使用 {create_group=True}/set_pgid 生成进程挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61856063/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com