我正在编写一个简单的程序来在三个命令之间进行传递。对于每个命令,我派生一个孩子并在其中调用execvp,而父母等待execvp完成(注意,因为我分叉了3次,所以我调用了wait(NULL)3次)
我创建了两个管道pipe_A和pipe_Z。流程类似于:
命令1将其输出写入pipe_A, 命令2从pipe_A读取输入,然后将输出写入pipe_Z,然后 命令3从pipe_Z读取输入,然后写入标准输出。
问题是程序在第二个等待(NULL)时卡住了。在我的代码中,这是'printf(“ reached \ n”);'之后的行。该程序不会终止,并且会阻塞,直到我发送中断为止。
如果我用相同的命令“ echo hello”替换所有三个命令,它不会执行很多操作,但程序至少会结束。
我只用两个命令(“ echo hello”和“ wc”)编写了一个较小的程序(相同的结构),它也卡在了第一次等待(NULL)上。但是,我注意到,如果我在主程序中的第二个命令上调用execvp,则输出将按预期进行打印。但是,该程序也会立即终止(由于execvp),所以这不是我想要的方式。
我怀疑由于缺乏对管道如何工作或wait(NULL)函数调用如何工作的了解而犯了一个非常明显的错误。
int main(){
//### COMMANDS AS STRING ARRAYS ###
char * cmds1[3] = {"echo","hello", NULL};
char * cmds2[3] = {"cat","-",NULL};
char * cmds3[3] = {"wc", NULL};
//### CREATING PIPES ###
int pipe_A[2];
int pipe_Z[2];
pipe(pipe_A);
pipe(pipe_Z);
//### FIRST FORK FOR FIRST EXECVP ###
int pid = fork();
//Child process
if(pid==0){
close(pipe_A[0]);
dup2(pipe_A[1], STDOUT_FILENO);
execvp(cmds1[0],cmds1);
exit(1);
}
//Parent process
else if(pid >0){
wait(NULL);
}
//### SECOND FORK FOR SECOND EXECVP ###
int pid2 = fork();
//Child process
if(pid2==0){
close(pipe_A[1]);
dup2(pipe_A[0],STDIN_FILENO);
close(pipe_Z[0]);
dup2(pipe_Z[1], STDOUT_FILENO);
execvp(cmds2[0],cmds2);
exit(1);
}
//Parent process
else if(pid2>0){
printf("reached\n");
wait(NULL);
}
//### THIRD FORK FOR THIRD EXECVP ###
int pid3 = fork();
//Child process
if(pid3==0){
close(pipe_Z[1]);
dup2(pipe_Z[0],STDIN_FILENO);
execvp(cmds3[0],cmds3);
exit(1);
}
//Parent process
else if(pid2 >0){
wait(NULL);
}
//### END OF PROGRAM ###
return 0;
}
预期输出为“ 1 1 6”,但程序不会终止。
答案 0 :(得分:3)
您没有在父进程中关闭管道的写端,因此它们中的任何一个都没有真正关闭,因此所有子级都没有获得EOF,因此所有子级都没有(除了{{1} }(不读取输入内容)退出。
答案 1 :(得分:0)
您说:
...而父级等待execvp完成
和execvp(2)
从不等待,以完成execvp(2)
系统调用,但要等待execvp(2)
开始的整个程序结束。
顺便说一句,stderr
在没有错误的情况下不会返回。一件好事是在exit(1)
中显示失败的原因,而不是制作execvp(...);
fprintf(stderr, "EXECVP: %s: %s\n", cmds?[0], strerror(errno));
exit(1);
,因为退出代码是由用户检查的,而且您不知道发生了什么。像
fork()
将非常有效。它只是在当前进程中启动另一个程序,即您从父close(2)
所生的孩子。
您的第一个问题是,您未使用的文件描述符在父级中保持打开状态,因此管道的读取器进程将被阻塞,等待更多输入,直到管道的写入端全部关闭为止。可以进行写作的流程(其中包括父项)。您在父进程中创建管道,因此,它们也将在子进程中获得。在子级中,您wait(2)
的未使用端,但在父级中不这样做(并且必须在等待之前执行),因此正在读取的子级在编写器时将收到文件结尾端关闭了写端,因为这样,不再有任何写进程(就像现在是父进程一样)
第二,这是一个简单的情况,其中三个进程将强制依次结束,但是即使在这种情况下,您也要等待它们完成才能启动下一个进程。由于流程是相互关联的,因此只有在所有流程都有机会交换信息之后。在这种情况下,您需要等待一个完成,然后再启动第二个,而第二个将不会读任何东西,除非它还活着。在您的情况下,第一个close(2)
正在等待第一个孩子退出,但是第一个孩子不能fork()
(在关闭时被阻止),因为没有人打开管道进行读取(嗯,父级确实这样做了,但预期读取该信息的过程是第二个孩子)。如果您等待第一个进程终止,那么两个进程将永远不会同时存活,因为您要等到第一个进程结束之后再生成第二个进程。
最好是等到最后一遍……就像你说的那样,你已经取得了三个成功 wait()
,所以你必须做三个{{1} }才能正确结束。在这种情况下
for (i = 0; i < 3; i++) wait(NULL);
是正确的(但在末尾)。 (即使任何前叉未成功,等待也会检测到该错误并失败并显示错误,因此您不必检查错误)