我试图创建一个分叉的程序,当父级等待子进程终止时,这个子进程再次分叉,然后执行两个执行程序。程序上有一个Pipe,我已经检查了程序中每个dup2()和pipe()的返回值 - 这里省略了它们,使它看起来更简洁。问题是我只得到ls -a |的结果在程序完成后排序-r。 代码是:
#include <cstdio>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char *argv[]) {
printf("Shell> \n"); fflush(stdout);
pid_t pid1;
pid_t pid2;
int status = 0;
int fd[2];
if(pipe(fd) < 0) {
printf("FATAL ERROR.\n");
}
pid1 = fork();
if(pid1 > 0) { // Parent
waitpid(pid1, &status, 0);
printf("\t\t------PID1 Complete-------\n\n");
}
else { // Child
if(pid1 == 0) {
printf("ON CHILD\n");
pid2 = fork();
if(pid2 > 0) { // Child -> Parent
printf("ON CHILD-Parent\n");
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
waitpid(pid2, &status, 0);
printf("ON CHILD-Parent after wait\n");
execlp("sort", "sort", "-r", NULL);
perror("Problem with execlp\n");
exit(1);
}
else { // Child -> Child
printf("ON CHILD->Child\n");
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
execlp("ls", "ls", "-a", NULL);
perror("Problem with execvp\n");
exit(1);
}
} // End of if(pid1 == 0)
} // End of Child
printf("\nEnd of program.\n");
return 0;
}
我目前的输出是:
Shell>
ON CHILD
ON CHILD-Parent
ON CHILD->Child
ON CHILD-Parent after wait
我认为问题在于等待,但我无法弄清楚如何使这项工作。有任何想法吗?谢谢!
答案 0 :(得分:3)
问题是您在祖父母进程中调用pipe
。在孙子进程(ls -a)退出之后,父进程(sort -r)无限期地阻塞等待从管道读取更多输入,因为某个进程 - 祖父进程 - 将一个打开的描述符保存到管道的写端。
如果在祖父进程中关闭管道描述符,或者更好的是将pipe
调用移动到第一个分叉进程中,那么排序进程将在最后一个具有开放描述符的进程结束时终止。管道退出(DEMO):
int main() {
// Turn off buffering of stdout, to help with debugging
setvbuf(stdout, NULL, _IONBF, 0);
printf("Shell> \n");
pid_t pid1 = fork();
if(pid1 < 0) {
perror("fork failed");
}
if(pid1 > 0) { // Parent
int status;
waitpid(pid1, &status, 0);
printf("\t\t------PID1 Complete (%d) -------\n\n", status);
} else { // Child
printf("ON CHILD\n");
int fd[2];
if(pipe(fd) < 0) {
perror("pipe failed");
return 1;
}
pid_t pid2 = fork();
if(pid2 < 0) {
perror("fork failed");
}
if(pid2 > 0) { // Child -> Parent
printf("ON CHILD-Parent\n");
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
execlp("sort", "sort", "-r", NULL);
perror("Problem with execlp");
return 1;
} else { // Child -> Child
printf("ON CHILD->Child\n");
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
execlp("ls", "ls", "-a", NULL);
perror("Problem with execvp");
return 1;
}
}
printf("\nEnd of program.\n");
}
该程序的另一个问题是@nategoose评论的问题:如果&#34; ls -a&#34;的输出,对waitpid
的调用可能导致死锁。太大而无法放入管道的缓冲区。没有理由等待,所以应该简单地将其消除。
答案 1 :(得分:1)
这不是一个真正的答案,但我有一些我想分享的内容。
为了确保您的输出按照它应该的顺序排出,我冲洗的次数比你多了很多。请记住,当您调用fork()
,clone()
,vfork()
,dup()
,dup2()
,close()
或任何{{}等函数时1}}你正在做的函数系列,它们属于C运行时环境,包括exec()
。如果你这样做:
stdio
你很可能会得到:
printf("cat");
fork();
fflush(stdout);
作为你的输出,因为你已经复制了stdout结构,包括所有缓冲的数据,所以除非stdio决定在printf函数结束之前无论如何都要刷新,然后&#34; cat&#34;在每个进程的stdout缓冲区中。
还有一个事实是,当您在catcat
系列中运行某个功能时,数据可以保持缓冲状态,在您的程序被新程序替换之前,您的数据可能无法刷新。当您的程序被exec
或ls
替换时,stdout中待处理的所有数据都会永远丢失。
此外,当你使用sort
时,你有另一个问题,因为你正在从stdio下面交换文件描述符,所以它可能还没有刷新,数据可能最终被刷新到新文件之后DUP。
由于这些事情,你应该拨打更多dup
电话,但我不认为这是你的问题。