You created the pipes after the 1st fork (but before the 2nd).
Which means that you called pipe
4 times, 2 times in each process that exist after the 1st fork.
So fd1
and fd2
have one set of values for main process (the one for which pid1>0
, aka the one that executes ls
), and another for the child and grandchild processes (the 2nd fork is done after the creation of the pipes, so no problem here: the two processes that executes sort
and grep
do share the same file descriptors).
So for the sort | grep
part, no problem. The output of sort is fd2[1]
, while the input of grep
is fd2[0]
, as you want, fd2
being the result of the same pipe
call, that was executed before the 2nd fork, and therefore shared between those 2 processes.
But for the ls | sort
part, what you are doing is exactly as if you did something like
if(fork()){
int fd[2];
pipe(fd);
close(fd[0]);
dup2(fd[1], 1);
printf("Hello\n");
exit(0);
}else{
int fd[2];
pipe(fd);
close(fd[1]);
dup2(fd[0], 0);
char s[100];
scanf("%s", s);
printf("Should be hello=%s\n", s);
exit(0);
}
Wouldn't work as expected. There is no relationship whatsoever between the fd
of the two processes.
So you need to create the pipes before the fork.
At least, the pipes that you intend to share among the processes that will be created by this fork.
I think I know why you did this strange thing. Because of another caveat with 3 process pipe chain: we often see people creating all their pipe before forking twice, having problem making their code work. For another reason: they usually forget that their pipe exist in 3 process, and they need to close all those they don't use. Even in processes that have nothing to do with fd1
, both end of fd1
must be closed.
Edit:
Another strange thing you did, is closing even fd1[WRITE]
even in the process where you want to use it (just after dup2). You can't do that. It is not like after dup2
you can close the original pipe because you would have a copy, or something like that. It is a copy of a descriptor to the same file. If you close the file, it is closed for the copy too.
A rewrite of your code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#define WRITE 1
#define READ 0
int main(int argc, char** argv)
{
int fd1[2], fd2[2];
pid_t pid1,pid2;
if( pipe(fd1) < 0)
{
perror("pipe 1");
exit(-1);
}
if( (pid1 = fork()) < 0)
{
perror("fork");
exit(-1);
}
if( pid1 == 0 ){
if( pipe(fd2) < 0)
{
perror("pipe 2");
exit(-1);
}
pid2 = fork();
}
if(pid1>0)
{
// I need to close fd1[READ] that I don't use (and that "sort" will use)
// I must keep fd1[WRITE] that I use
// I leave fd2[READ/WRITE] alone, since that variable is unitialized here (I've called "pipe(fd2)" only in the case
// pid1==0 which it is not here)
close(fd1[READ]);
dup2(fd1[WRITE],STDOUT_FILENO);
execlp("ls","ls",NULL);
perror("ls");
exit(-1);
}
if(pid2>0)
{
// I need to read fd1, but not write it (it is ls that need to write it).
// So before doing anything, I close fd1[WRITE]. Must I must keep the other end, fd1[READ] open
close(fd1[WRITE]);
dup2(fd1[READ],STDIN_FILENO);
// Likewise, I need to write on fd2[WRITE], and have "grep" read on the other end.
// So I close fd2[READ] this side, and keep fd2[WRITE] open
close(fd2[READ]);
dup2(fd2[WRITE],STDOUT_FILENO);
execlp("sort","sort",NULL);
perror("sort");
exit(-1);
}
if(pid2==0)
{
// Last process. It needs to read fd2[READ], that I keep open, and don't need to write on fd2[WRITE] (that is "sort" job)
// so I close fd2[WRITE]
// I don't really case for fd1
// Situation is not exactly symetrical that for 1st process and fd2 tho. Because for 1st process, fd2 what not even initialized.
// While here, even if we don't need it, fd1 has been created by our grandfather. We shared it from our father, that shared it from its father
// So, since we use none of fd1, we need to close both.
close(fd1[READ]);
close(fd1[WRITE]);
close(fd2[WRITE]);
dup2(fd2[READ],STDIN_FILENO);
execlp("grep","grep","r",NULL);
perror("grep");
exit(-1);
}
}
Note that this is not the only solution. I could have created pipe(fd2)
unconditionally before even the 1st fork, and then close both fd2[0]
and fd2[1]
in the "ls" process. That would have been ok too.
The points are:
- If you want to write on a
fd[1]
in a process, and read what have been written, from another process, on fd[0]
, then those process must share the same fd
values, issued by the same pipe
call; so pragmatically, that means that pipe(fd)
must have been called before the fork
that separated those two processes
- Don't close the
fd?[?]
that you use. Even if you use them only through another fd on which you dup2
the fd?[?]
.
- Close any
fd?[?]
that exist in a process, that is that have been created by a pipe
call (you must mentally keep tracks of which process have which fd
) and you don't need.