【问题标题】:Why is STDIN not propagated to child process of different process group?为什么 STDIN 不传播到不同进程组的子进程?
【发布时间】:2020-03-14 00:52:47
【问题描述】:

下面是执行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. 父进程产生cat 的子进程,并且
  2. 孩子开始与终端交互。

然而,发生的事情是:

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

当对setpgid() 的调用被注释掉时,一切正常。

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

  • 孩子cat 正在尝试读取标准输入并正在等待(因此停止),但是
  • 终端的输入首先传递给bash,然后传递给上面的程序,但是没有传递给cat,可能是因为bash不认识它的孙子cat,因为它的不同进程组。

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

我想问一下:

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

【问题讨论】:

  • 似乎有问题。如果我没记错的话,来自终端的信号会发送到前台进程组,即接受输出和输入的进程组。在这里您似乎想将两者解耦,这不符合我的理解(这并不完美,我希望至少能够在您的程序中使用tcsetpgrp 来启动一个新组并更改前台进程组到那个组,但我错了)。
  • 我无法在 Ubuntu 19.04 上重现该问题。我以./program &lt; inputfile 运行程序,它打印inputfile 的内容。当我使用strace as strace -f ./program &lt; inputfile 运行它时,我可以看到它执行/usr/bin/cat 并且cat 从标准输入读取输入文件并将数据写入标准输出。
  • @AProgrammer 你是对的! tcsetpgrp 是我需要的。谢谢你让我知道。关于tcsetpgrp不起作用的可以参考another answer in SO
  • @Namnamseo,谢谢。我将不得不深入研究该答案以完成我的理解。如果你的问题解决了,我建议你写一个答案。

标签: c linux stdin


【解决方案1】:

根据 cmets 的建议,前台进程组(假定所有 STDIN)可以通过 tcsetpgrp() 更改。

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

但是,正如SO question 中所描述的,当孩子(还不是前台)调用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

【讨论】:

    猜你喜欢
    • 2016-04-03
    • 2014-03-20
    • 1970-01-01
    • 1970-01-01
    • 2012-06-18
    • 1970-01-01
    • 2014-06-22
    • 2016-04-05
    • 2013-08-02
    相关资源
    最近更新 更多