我正在尝试编写一个 C 程序来执行类似管道的操作
ls | wc | wc
。我已经完成了ls | wc
,而且效果很好,但我不明白为什么我的程序停止在指定行的子进程上。
int main (void)
{
pid_t pid_fils, pid_pfils;
int fd[2], fd2[2];
if(pipe(fd)==-1 || pipe(fd2)==-1)
{
printf("pipe failed!");
return 1;
}
printf("program started\n");
pid_fils=fork();
if(pid_fils==0)
{
pid_pfils=fork();
if(pid_pfils==0)
{
//action3
printf("I am the grandson\n");
close(fd[0]);//close read side
dup2(fd[1],1);//connect write with stdout
close(fd[1]);//close write side
execlp("ls","ls",(char*)0);
//execvp("ls",argv3);
return 0;/*actions grandson*/
}
else
{
//action2
printf("I am the son\n");
wait();
printf("son, wait ok\n");
>close(fd[1]); //close read side
>dup2(fd[0],0); //connect write with stdin
>close(fd[0]); //close read side
///////pipe2////
> close(fd2[0]); //close read side
>dup2(fd2[1],1); //connect write with stdout/*it stops here -can't display "ok!"*/
printf("ok!\n");
>close(fd2[1]); //close write side
execlp("wc","wc",(char*)0);
printf("error exec returned!\n");
return 0;
}
}
else
{
///action1
printf("I am the parent\n");
wait();
printf("parent,wait ok\n");
close(fd2[1]); //close write side,
dup2(fd2[0],0); //connect read with stdin
close(fd2[0]); //close read side
execlp("wc","wc",(char*)0);
return 0;/*the parent*/
}
return 1;
}
确保关闭所有未使用的描述符。对于您的情况,最简单的解决方案是将 pipeline(fd) 的创建移至第一个 if 块(在第一个子流程中)。问题是,只要任何进程可能写入管道,读取器就不会收到 EOF,因此不会终止。
if(pipe(fd2)==-1)
{
printf("pipe failed!");
return 1;
}
printf("program started\n");
pid_fils=fork();
if(pid_fils==0)
{
if(pipe(fd)==-1)
{
printf("pipe failed!");
return 1;
}
pid_pfils=fork();
我还应该提到,您可能需要重新考虑等待调用。不确定您打算用它们做什么,但您不希望“ls”进程阻止输出,因为阅读器尚未启动。
dup2(fd2[1],1);
上面的行将首先关闭描述符 1 处的文件,然后将描述符从 fd2[1] 复制到 1。
1 是标准输出。这意味着该调用关闭了标准输出。
printf 打印到 stdout,这意味着 printf 打印到 1,现在已分配给管道 fd2
所以你的ok进入了管道而不是在屏幕上。
尝试
//action2
printf("I am the son\n");
wait();
printf("son, wait ok\n");
close(fd[1]); //close read side
dup2(fd[0],0); //connect write with stdin
close(fd[0]); //close read side
///////pipe2////
int my_terminal_out = dup(1);
close(fd2[0]); //close read side
dup2(fd2[1],1); //connect write with stdout/*it stops here -can't display "ok!"*/
fprintf(my_terminal_out, "ok!\n");
close(fd2[1]); //close write side
未经测试。此外,您还应该测试其余代码是否存在类似的错误。
+DrC 所说的。
扩展这个SO答案,我们可以创建一个额外的管道,分叉另一个进程,并相应地连接管道。
// two-pipes.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
// C implementation for the `ls -la | wc | wc` pipeline.
int main(int argc, char *argv[]) {
// We declare two pipes, one for each pair of processes, i.e.,
// ls -al pipefd1 wc pipefd2 wc
int pipefd1[2];
int pipefd2[2];
pid_t ls_pid, wc_pid1, wc_pid2;
pipe(pipefd1);
pipe(pipefd2);
// CHILD PROCESS: ls
if ((ls_pid = fork()) == 0) {
// We close-and-then-connect STDOUT to pipefd1's write end so the
// process write to the pipe instead of the screen.
dup2(pipefd1[1], STDOUT_FILENO);
// Since STDOUT_FILENO also refers to pipefd's write end, we can
// close this file descriptor; it's no longer needed.
close(pipefd1[1]);
// The process doesn't use these file descriptors so we close them.
close(pipefd1[0]);
close(pipefd2[0]);
close(pipefd2[1]);
// Execute the `ls` command, and exit if errors out.
if ((execl("/bin/ls", "ls", "-al", (char *) NULL)) < 0) exit(0);
}
else if (ls_pid < 0) {
fprintf(stderr, "failed to fork ls process");
exit(0);
}
// CHILD PROCESS: wc (1)
if ((wc_pid1 = fork()) == 0) {
// We close-and-then-connect STDIN and STDOUT to pipefd1's read and
// and pipefd2's write end so the process read from pipefd1 and write
// to pipefd2 instead of from the keyboard and to the screen respectively.
dup2(pipefd1[0], STDIN_FILENO);
dup2(pipefd2[1], STDOUT_FILENO);
// Since STDIN_FILENO and STDOUT_FILENO also refer to pipefd1's read
// end and pipefd2's write end respectively, we can close these file
// descriptors.
close(pipefd1[0]);
close(pipefd2[1]);
// The process doesn't use these file descriptors so we close them.
close(pipefd1[1]);
close(pipefd2[0]);
// Execute the `wc` command, and exit if errors out.
if ((execl("/usr/bin/wc", "wc", (char *) NULL)) < 0) exit(0);
}
else if (wc_pid1 < 0) {
fprintf(stderr, "failed to fork wc process");
exit(0);
}
// CHILD PROCESS: wc (2)
if ((wc_pid2 = fork()) == 0) {
// We close-and-then-connect STDIN to pipefd2's read end so the process
// read from the pipe instead of from the keyboard.
dup2(pipefd2[0], STDIN_FILENO);
// Since STDIN_FILENO also refers to the pipefd2's read end, we can
// close this file descriptor; it's no longer needed.
close(pipefd2[0]);
// The process doesn't use these file descriptors so we close them.
close(pipefd1[0]);
close(pipefd1[1]);
close(pipefd2[1]);
// Execute the `wc` command, and exit if errors out.
if ((execl("/usr/bin/wc", "wc", (char *) NULL)) < 0) exit(0);
}
else if (wc_pid1 < 0) {
fprintf(stderr, "failed to fork wc process");
exit(0);
}
// PARENT PROCESS
// The parent process isn't using the pipes, however these descriptors are
// another references to the pipe's read and write ends and we must close
// them. Otherwise, it doesn't send the EOF so the children can continue
// (children block until all input has been processed).
close(pipefd1[0]);
close(pipefd1[1]);
close(pipefd2[0]);
close(pipefd2[1]);
// The parent process waits for the three child processes to finish before
// exiting.
int ls_status, wc_status1, wc_status2;
pid_t ls_wpid = waitpid(ls_pid, &ls_status, 0);
pid_t wc_wpid1 = waitpid(wc_pid1, &wc_status1, 0);
pid_t wc_wpid2 = waitpid(wc_pid2, &wc_status2, 0);
// Return main's status based on whether the parent process waited both
// child processes successfully. Status based only on `ls_status`.
return
ls_pid == ls_wpid && WIFEXITED(ls_status) &&
wc_pid1 == wc_wpid1 && WIFEXITED(wc_status1) &&
wc_pid2 == wc_wpid2 && WIFEXITED(wc_status2)
? WEXITSTATUS(ls_status)
: -1;
}
编译并运行:
$ ls -la
total 112
drwxr-xr-x 8 256 Nov 20 13:52 .
drwxr-x---+ 91 2912 Nov 20 13:55 ..
-rwxr-xr-x 1 33896 Nov 20 13:48 a.out
-rw-r--r-- 1 45 Nov 17 15:33 error.log
-rw-r--r--@ 1 2390 Nov 20 09:51 ls-wc-pipes.c
-rw-r--r-- 1 116 Nov 17 16:01 out.log
-rw-r--r--@ 1 2647 Nov 20 09:49 so-pipes.c
-rw-r--r--@ 1 4021 Nov 20 13:52 two-pipes.c
$ gcc two-pipes.c && ./a.out
1 3 25