【发布时间】: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(sh 或 bash)中运行时,我预计:
- 父进程产生
cat的子进程,并且 - 孩子开始与终端交互。
然而,发生的事情是:
- 父级生成
cat没有问题,但是 - 孩子停止(进程状态代码
T在ps),并且 - 孩子不接受来自终端的输入,并且
- 孩子对
SIGINT、SIGSTOP或SIGQUIT中的任何一个都没有反应,只会被SIGKILL杀死。
当对setpgid() 的调用被注释掉时,一切正常。
我怀疑该行为是由以下原因引起的:
- 孩子
cat正在尝试读取标准输入并正在等待(因此停止),但是 - 终端的输入首先传递给
bash,然后传递给上面的程序,但是没有传递给cat,可能是因为bash不认识它的孙子cat,因为它的不同进程组。
当然,删除setpgid() 调用是最简单的解决方案。不幸的是,有一些原因;主要是为了拦截parent中的一些信号(如SIGINT或SIGSTOP)。换句话说,<Ctrl-C> 不应该杀死cat,而是以某种方式向上面的程序发出信号。 (上面的程序中没有信号处理程序,是的,用于说明目的。)
我想问一下:
- 这是正确的吗?
- 不管是不是,我怎样才能让
cat接收来自标准输入的输入?
【问题讨论】:
-
似乎有问题。如果我没记错的话,来自终端的信号会发送到前台进程组,即接受输出和输入的进程组。在这里您似乎想将两者解耦,这不符合我的理解(这并不完美,我希望至少能够在您的程序中使用
tcsetpgrp来启动一个新组并更改前台进程组到那个组,但我错了)。 -
我无法在 Ubuntu 19.04 上重现该问题。我以
./program < inputfile运行程序,它打印inputfile的内容。当我使用straceasstrace -f ./program < inputfile运行它时,我可以看到它执行/usr/bin/cat并且cat从标准输入读取输入文件并将数据写入标准输出。 -
@AProgrammer 你是对的!
tcsetpgrp是我需要的。谢谢你让我知道。关于tcsetpgrp不起作用的可以参考another answer in SO。 -
@Namnamseo,谢谢。我将不得不深入研究该答案以完成我的理解。如果你的问题解决了,我建议你写一个答案。