问题
I'm learning to use pipes and following along with this code on pipes. The program makes two child processes using fork. The first child runs 'ls' command and outputs to pipe1. The second reads from pipe1 runs 'wc' and outputs to stdout.
I'm just trying to add a third process in the middle that reads from pipe1 and outputs to pipe2. Basically what I'm trying to do
ls | cat | wc -l
What I'm trying to do:
(ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout
Nothing ever prints to stdout and the program never exits.
Here's my code with the changes for process #3
int
main(int argc, char *argv[])
{
int pfd[2]; /* Pipe file descriptors */
int pfd2[2];
if (pipe(pfd) == -1) /* Create pipe */
perror("pipe");
if (pipe(pfd2) == -1) /* Create pipe */
perror("pipe");
/*
Fork process 1 and exec ls command
write to pfd[1], close pfd[0]
*/
switch (fork()) {
case -1:
perror("fork");
case 0:
if (close(pfd[0]) == -1)
perror("close 1");
// dup stdout on pfd[1]
if (pfd[1] != STDOUT_FILENO) {
if (dup2(pfd[1], STDOUT_FILENO) == -1)
perror("dup2 2");
if (close(pfd[1]) == -1)
perror("close 4");
}
execlp("ls", "ls", (char *) NULL);
perror("execlp ls");
default:
break;
}
/*
* Fork process 2 and exec wc command
read from pfd[0], close pfd[1]
write to pfd[1], close pfd2[0]
*/
switch (fork()) {
case -1:
perror("fork");
case 0:
// read from pfd[0]
if (close(pfd[1]) == -1)
perror("close 3");
if (pfd[0] != STDIN_FILENO) {
if (dup2(pfd[0], STDIN_FILENO) == -1)
perror("dup2 2");
if (close(pfd[0]) == -1)
perror("close 4");
}
if (pfd2[1] != STDOUT_FILENO) {
if (dup2(pfd2[1], STDOUT_FILENO) == -1)
perror("dup2 2");
if (close(pfd2[1]) == -1)
perror("close 4");
}
execlp("cat", "cat", (char *) NULL);
perror("execlp cat");
default:
break;
}
/*
* Fork process 3
*/
switch (fork()) {
case -1:
perror("fork");
case 0:
if (close(pfd2[1]) == -1)
perror("close 3");
if (pfd2[0] != STDIN_FILENO) {
if (dup2(pfd2[0], STDIN_FILENO) == -1)
perror("dup2 2");
if (close(pfd2[0]) == -1)
perror("close 4");
}
execlp("wc", "wc", "-l", (char *) NULL);
perror("execlp wc");
default:
break;
}
/* Parent closes unused file descriptors for pipe, and waits for children */
if (close(pfd[0]) == -1)
perror("close 5");
if (close(pfd[1]) == -1)
perror("close 6");
if (close(pfd2[0]) == -1)
perror("close 5");
if (close(pfd2[1]) == -1)
perror("close 6");
if (wait(NULL) == -1)
perror("wait 1");
if (wait(NULL) == -1)
perror("wait 2");
if (wait(NULL) == -1)
perror("wait 3");
exit(EXIT_SUCCESS);
}
回答1:
The problem is that you did not close pfd[1]
in process 3, add close(pfd[1]);
after case 0 in that process 3 will fix it.
In process 3, that cat
will read from pfd[0]
, however there are four pfd[1]
in those processes:
process 0
this is the main process,
pfd[1]
in this process will be closed by that close beforewait()
.process 1
after
ls
finished,pfd[1]
in this process will be closed automatically by the operating system.process 2
pfd[1]
has been closed before executingcat
.process 3
pfd[1]
is open in this process whilewc
is running, and this is what happened at that moment:- in process 2,
cat
tries to readpfd[0]
for data frompfd[1]
- in process 3,
wc
tries to readpfd2[0]
for data frompfd2[1]
- because
pdf[1]
still open in process 3, and nothing will be written to it, reading frompfd[0]
in process 2 (cat) will wait forever - because
cat
in process 3 still alive, reading frompfd2[0]
in process 3 (wc) will wait (forever)
- in process 2,
As you can see, you have a deadlock between process 2 (cat) and process 3 (wc) because of file descriptor leak. To break this deadlock, you just need to close pfd[1]
in process 3 before you run wc
, after that:
cat
in process 2 will exit afterls
in process 1 exits, because there is nothing left for it (cat) to read- after
cat
in process 2 exits,wc
in process 3 will also exit, because there is nothing left for it (wc) to read - after that, the main process (parent process) will exit, and the program will finish.
It is possible that there are more than one write ends for the read end of a pipe, unless all these write ends are closed, end-of-file will not be delivered to the read end, and the reader will just wait for more data to come. If there is nothing to come, that reader will wait forever.
来源:https://stackoverflow.com/questions/23419128/learning-pipes-exec-fork-and-trying-to-chain-three-processes-together