In C, why is my program stuck on wait() when trying to pipe multiple commands?
我正在编写一个简单的程序以在三个命令之间进行传递。对于每个命令,我派生一个孩子并在其中调用execvp,而父级则等待execvp完成(注意,因为我分叉了3次,所以我调用了wait(NULL)3次)
我创建了两个管道pipe_A和pipe_Z。流程类似于:
命令1将其输出写入pipe_A,
命令2从pipe_A读取输入,然后将输出写入pipe_Z,然后
命令3从pipe_Z读取输入,然后写入标准输出。
问题是程序在第二个等待(NULL)时卡住了。在我的代码中,这是'printf("
");'。程序不会终止,并且一直停留到我发送中断为止。
如果我用相同的命令" echo hello"替换所有三个命令,它不会执行很多操作,但是程序至少会结束。
我只用两个命令(" echo hello"和" wc")编写了一个较小的程序(相同的结构),它也卡在了第一次等待(NULL)上。但是,我注意到,如果我在主程序中的第二条命令上调用execvp,则输出将按预期打印。但是,该程序也会立即终止(由于execvp),所以这不是我想要的方式。
我怀疑由于不了解管道的工作方式或wait(NULL)函数调用的工作方式而犯了一个非常明显的错误。
int main(){
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | //### 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\ "); 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",但程序不会终止。
您没有在父进程中关闭管道的写端,因此它们都没有真正完全关闭,因此所有子级都没有获得EOF,因此所有子级都没有(除了
您说的是:
... while the parent waits for execvp to finish
和
BTW,
之类的东西
将非常有效。它只是在当前进程中启动另一个程序,即您从父级
-
您的第一个问题是,您未使用的文件描述符在父级中保持打开状态,因此管道的读取器进程将被阻塞,等待更多输入,直到管道的写入端被所有为其打开的进程所关闭。写作(包括父母)。您在父进程中创建管道,因此,它们也将在子进程中获得。在子级中,您
close(2) 管道的未使用端,但是您在父级中不做相同的操作(并且必须在等待之前执行),因此正在读取的子级将在编写器端收到文件的结尾关闭写面,因为这样一来,不再有任何写程序(就像现在是父进程一样) -
第二,这是一个简单的情况,其中三个进程将强制依次结束,但是即使在这种情况下,您也要等待它们完成才能启动下一个进程。由于流程是相互关联的,因此只有在所有流程都有机会交换信息之后。在这种情况下,您需要等待一个完成,然后再启动第二个,而第二个将不会读任何东西,除非它还活着。在您的情况下,第一个
wait(2) 正在等待第一个孩子退出,但是第一个孩子不能close(2) (它在关闭时被阻止),因为没有人打开管道进行读取(好吧,父对象这样做了,但是预期正在读取该信息的过程是第二个孩子)。如果您等待第一个进程终止,则两个进程将永远不会同时存活,因为您要等到第一个进程结束后再生成第二个进程。
最好是等到它们全部结束时...正如您所说,您已经成功完成了三个
1 | for (i = 0; i < 3; i++) wait(NULL); |
是正确的(但在末尾)。 (即使任何派生都不成功,等待也会检测到此错误并失败并显示错误,因此您不必检查错误)