我的c ++程序调用fork(),子进程立即执行另一个程序。我必须与子进行交互,但同时终止其父进程,因为它的可执行文件将被替换。我不知何故需要让孤儿回到前台,以便我可以通过bash与它进行交互 - 我目前只得到它的输出。因此,我需要将父级发送到后台,将子级发送到前台,然后终止父级,或者在父级终止时立即将子级发送到后台。
据我所知,我必须在其父级终止之前将其设置为进程组组长。 通过this thread的慷慨借款,我到达了下面的测试场地(注意,这不是完整的程序 - 它只是概述了程序):
int main(int argc, char *argcv[])
printf("%i\n", argc);
printf("\nhello, I am %i\n", getpid());
printf("parent is %i\n", getppid());
printf("process leader is %i\n", getsid(getpid()));
int pgrp;
std::stringstream pidstream;
pidstream << tcgetpgrp(STDIN_FILENO);
pidstream >> pgrp;
printf("foreground process group ID %i\n", pgrp);
if(argc==1)
{
int child = fork();
if(!child) {execl("./nameofthisprogram","nameofthisprogram", "foo", NULL);}
else
{
signal(SIGTTOU, SIG_IGN);
usleep(1000*1000*1);
tcsetpgrp(0, child);
tcsetpgrp(1, child);
std::stringstream pidstream2;
pidstream2 << tcgetpgrp(STDIN_FILENO);
pidstream2 >> pgrp;
printf("foreground process group ID %i\n", pgrp);
usleep(1000*1000*3);
return 0;
}
}
// signal(SIGTTOU, SIG_IGN); unnecessary
int input;
int input2;
printf("write something\n");
std::cin >> input;
printf("%i\n", input);
usleep(1000*1000*3);
printf("%i\n", input);
printf("write something else\n");
std::cin >> input2;
usleep(1000*1000*3);
printf("%i\n", input2);
return 0;
使用上面的代码,在提示输入第一个输入后,父级会死掉。如果我将我的答案推迟到父母的死亡之后,它会拿起第一个输入字符并再次打印出来。对于input2,程序不等待我的输入。 因此,似乎在第一个字符之后,输入完全终止。 我是否接近这个根本错误,或者只是重新分配一些更多的ID并改变一些信号?
答案 0 :(得分:0)
我在这里看到了一些错误。
tcsetpgrp()
两次;它只需要调用一次。假设没有重定向,stdin和stdout都引用终端,因此任何一个调用都可以。使用上面的代码,在提示输入第一个输入后,父级会死掉。如果我然后将我的答案推迟到父母的死亡之后,它会拾取第一个输入字符并再次打印出来。对于input2,程序不等待我的输入。因此,似乎在第一个字符之后,输入完全终止。
你在这里观察到的是1.
的直接结果:由于两个进程都处于前台,因此它们都在竞争从stdin读取并且结果未定义。
我不知何故需要让孤儿回到前台,以便我可以通过bash与它进行交互 - 我目前只得到它的输出。
根据我的理解,你会期望在fork()/exec()
之后与执行的孩子互动。要做到这一点,孩子需要在自己的进程组中,并且需要放在前台。
int child = fork();
signal(SIGTTOU, SIG_IGN);
if (!child) {
setpgid(0, 0); // Put in its own process group
tcsetpgrp(0, getpgrp()); // Avoid race condition where exec'd program would still be in the background and would try to read from the terminal
execl("./nameofthisprogram","nameofthisprogram", "foo", NULL);
} else {
setpgid(child, child); // Either setpgid call will succeed, depending on how the processes are scheduled.
tcsetpgrp(0, child); // Move child to foreground
}
请注意,我们在父级和子级中调用setpgid()/tcsetpgrp()
对。我们这样做是因为我们不知道哪个将首先被安排,并且我们想要避免竞争条件,其中exec'ed程序将尝试从stdin读取(并因此接收将停止进程的SIGTTIN)有时间把它放在前台。我们也忽略SIGTTOU,因为我们知道孩子或父母将收到一个调用tcsetpgrp()
的人。