为什么STDIN不会传播到不同进程组的子进程?

Nam*_*seo 5 c linux stdin

以下是执行的程序的来源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;
}
Run Code Online (Sandbox Code Playgroud)

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

在shell中运行时(shbash),我期望:

  1. 父级产生的子进程cat,并且
  2. 孩子开始与终端进行交互。

但是,发生的是:

  1. 父母产卵cat没有问题,但是
  2. 子项已停止(进程状态代码Tps),并且
  3. 孩子不接受终端机的输入,并且
  4. 孩子没有任何回应SIGINTSIGSTOP或者SIGQUIT,只被冻死SIGKILL

当呼叫setpgid()被注释掉时,一切都按预期进行。

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

  • 孩子cat正在尝试阅读stdin并正在等待(因此已停止),但是
  • 终端的输入首先传递给bash,然后传递给上面的程序,但是不传递给cat,这可能是由于其不同的进程组而导致bash无法识别其子孙cat

当然,删除setpgid()呼叫是最简单的解决方案。不幸的是,有一些原因。主要是为了拦截父级中的某些信号(例如SIGINT或SIGSTOP)。换句话说,a <Ctrl-C>不应杀死,cat而是以某种方式通知上面的程序。(上面的程序中没有信号处理程序,是的,出于说明目的。)

我想问一下:

  1. 它是否正确?
  2. 不管是不是,我该如何cat接收来自stdin的输入?

Nam*_*seo 3

正如评论中所建议的,前台进程组(假设所有 STDIN)可以通过 进行更改tcsetpgrp()

该函数也可以从子进程中调用。否则,父级将不得不等待子级成功调用setpgid(),并且会发生并发问题。

然而,正如这个SO问题中所描述的,当子进程(还不是前台)调用时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;
}
Run Code Online (Sandbox Code Playgroud)

现在底层cat开始和终端交互了,问题就解决了。

uuu@hhh:~$ ./a
sajkfla
sajkfla
wjkelfaw
wjkelfaw
reaped PID = 774
uuu@hhh:~$ ./a
Run Code Online (Sandbox Code Playgroud)