Executing the ls | wc linux command in c program using 2 processes communicating trough a pipe
I'm currently having problems with the following exercise:
I want to "mimic" the pipe command line ls | wc
in linux bash with the following program.
What I do is:
- create a pipe
- create a reader child and writer child
- the writer child closes the pipe's reading side and redirects his stdout to the writing side of the pipe
- the reader child closes the pipe's writing side and makes the reading side of the pipe his stdin
- both children do an exec, the writer executes the
ls
program, passes the output through the pipe to the reader that executes thewc
program on that output.
When I do ls | wc
in linux terminal I get the following result:
8 8 101
But if I execute my program I get the following result:
0 0 0
Here is my program:
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <libgen.h>
#include <signal.h>
#include <errno.h>
int main(void){
int mypipe[2];
pid_t pid1, pid2;
if (pipe(mypipe)<0)
perror ("pipe error"), exit(1);
if ((pid1=fork())<0)
perror ("fork error"), exit(1);
else if (pid1==0) {
//reader child
close (mypipe[1]);
if (dup2(mypipe[0], STDIN_FILENO)!=STDIN_FILENO)
perror ("dup2 error"), exit(1);
close (mypipe[0]);
if (execlp("wc", "wc", NULL)<0)
perror("execlp1 error"), exit(1);
else { //pid >0, parent
if ((pid2=fork())<0)
perror ("fork error"), exit(2);
else if (pid2==0) {
//writer child
close(mypipe[0]);
if (dup2(mypipe[1], STDOUT_FILENO) != STDOUT_FILENO)
perror("dup2 error"), exit(1);
close (mypipe[1]);
if (execlp("ls", "ls", NULL)<0)
perror ("execlp error"), exit(1);
}
else { //parent
close(mypipe[0]);
close(mypipe[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
exit(0);
}
}
}
return 0;
}
What am I doing wrong? Thanks in advance for the answers!
Solution 1:
Your code is confused. You have many irrelevant headers, and repeat #include <errno.h>
. In the main()
function, you have:
int main(void)
{
int mypipe[2];
pid_t pid1, pid2;
if (pipe(mypipe) < 0)
perror("pipe error"), exit(1);
if ((pid1 = fork()) < 0)
perror("fork error"), exit(1);
else if (pid1 == 0)
{
// reader child
close(mypipe[1]);
if (dup2(mypipe[0], STDIN_FILENO) != STDIN_FILENO)
perror("dup2 error"), exit(1);
close(mypipe[0]);
if (execlp("wc", "wc", NULL) < 0)
perror("execlp1 error"), exit(1);
else // pid >0, parent
{
…
}
}
return 0;
}
The else if (pid1 == 0)
clause is executed by the child. It closes the write end of the pipe, duplicates the read end to standard input and closes the read end of the pipe. It then does execlp()
on wc
. Only if the code fails to execute wc
will the else
clause be executed, and then there is only the read end of the pipe left open. Meanwhile, the original process simply exits. That closes the pipe, so the wc
command gets no input, and reports 0 0 0
as a result.
You need to rewrite the code. The parent process should wait until both its children execute. Especially while debugging, you should not ignore the exit status of children, and you should report it.
Here's some code that works. Note that it avoids bushy decision trees — it is a linear sequence of if
/ else if
/ … / else
code. This is easier to understand, in general, than a bushy, multi-level set of conditions.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int fd[2];
pid_t pid1, pid2;
if (pipe(fd) < 0)
perror("pipe error"), exit(1);
else if ((pid1 = fork()) < 0)
perror("fork error"), exit(1);
else if (pid1 == 0)
{
/* ls */
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execlp("ls", "ls", (char *)0);
perror("exec ls");
exit(1);
}
else if ((pid2 = fork()) < 0)
perror("fork error"), exit(1);
else if (pid2 == 0)
{
/* wc */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execlp("wc", "wc", (char *)0);
perror("exec wc");
exit(1);
}
else
{
close(fd[0]);
close(fd[1]);
int status1;
int status2;
int corpse1 = waitpid(pid1, &status1, 0);
int corpse2 = waitpid(pid2, &status2, 0);
printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid1, corpse1, status1);
printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid2, corpse2, status2);
}
return 0;
}
A sample run of the program pipe41
on my machine produced:
$ pipe41
175 175 1954
ls: pid = 44770, corpse = 44770, exit status = 0x0000
ls: pid = 44771, corpse = 44771, exit status = 0x0000
$ ls | wc
175 175 1954
$