为什么 STDIN 没有传播到不同进程组的 child 个进程?

Why is STDIN not propagated to child process of different process group?

下面是执行cat:

的程序源码
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
  pid_t pid = fork();
  if (!pid) {
    // create a new process group
    setpgid(0, 0);

    execlp("cat", "cat", NULL);
  }

  pid_t reaped = wait(NULL);
  printf("reaped PID = %d\n", (int) reaped);
  return 0;
}

注意对 setpgid(0, 0) 的调用。

当 运行 在 shell(shbash)中时,我预计:

  1. parent 生成 cat 的 child 进程,并且
  2. child 开始与终端交互。

然而,发生的事情是:

  1. parent 生成 cat 没问题,但是
  2. child已停止(进程状态代码Tps),并且
  3. child 不接受来自终端的输入,
  4. child不回应SIGINTSIGSTOPSIGQUIT中的任何一个,只会被SIGKILL杀死。

当对 setpgid() 的调用被注释掉时,一切都如预期的那样。

我怀疑该行为是由以下原因引起的:

当然,删除setpgid()调用是最简单的解决方案。不幸的是有一些原因;主要是拦截parent中的一些信号(比如SIGINT或者SIGSTOP)。换句话说,<Ctrl-C> 不应该杀死 cat 而是以某种方式向上面的程序发出信号。 (上面的程序中没有信号处理程序,是的,用于说明目的。)

我想问:

  1. 这是正确的吗?
  2. 不管是不是,我怎样才能cat接收标准输入的输入?

如评论中所建议,可以通过 tcsetpgrp().

更改前台进程组(假定所有标准输入)

该函数也可以从 child 中调用。否则 parent 将不得不等待 child 执行成功的 setpgid() 调用并且会发生并发问题。

然而,正如SO question中所描述的,当child(还不是前台)调用tcsetpgrp时,它会得到一个SIGTTOU信号,根据手册tcsetpgrp 个。 SIGTTOU 的默认操作是停止进程,这应该手动忽略。

#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

int main()
{
  // ignore SIGTTOU
  signal(SIGTTOU, SIG_IGN);

  pid_t pid = fork();
  if (!pid) {
    // create a new process group
    setpgid(0, 0);
    tcsetpgrp(STDIN_FILENO, getpgid(0));

    execlp("cat", "cat", NULL);
  }

  pid_t reaped = wait(NULL);
  printf("reaped PID = %d\n", (int) reaped);
  return 0;
}

现在底层cat开始与终端交互,问题解决

uuu@hhh:~$ ./a
sajkfla
sajkfla
wjkelfaw
wjkelfaw
reaped PID = 774
uuu@hhh:~$ ./a