George
George

Reputation: 13

How do I use two pipes in Unix C?

I have a homework to do that says the following:

Write a program in C that creates a child who will also create a child, make a pipe between the three processes, the fist process(father) will connect the second(child) and the child will connect with the third (child of the child). Our program should display the total number of system users who use bash as default shell. The result of the program should be identical to the "cat / etc / passwd | grep" / bin / bash $ "| wc-l"

I am confused with the first child and the method that we close the first pipe and open the second in the same time. If you reply me with the right code I 'll undestand it right once.

Thank you.

Here is what I 've wrote so far:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int pid, pid2;
int fd[2];
int fd2[2];
char *arg[3];
char *arg2[3];
char *arg3[3];  
if (pipe(fd) == -1)
{
    perror("pipe");
    exit(1);
}
pid = fork();
if (pid == -1)
{
    perror("fork");
    exit(2);
}
if (pid == 0)
{
    if (pipe(fd2) == -1)
    {
        perror("pipe");
        exit(11);
    }
    pid2=fork();
    if(pid2 == -1)
    {
        perror("fork 2");
        exit(22);
    }
    if (pid2 == 0)
    {
        //i am child 2 (child of the child)
        close (fd2[1]);
        dup2 (fd2[0],0);
        close (fd2[0]);
        arg3[0] = "wc";
        arg3[1] = "-l";
        arg3[2] = NULL;
        execvp("wc", arg3);
        perror("execvp second child");
    }
    else
    {
        //i am child 1
        close (fd[1]);
        dup2(fd[0],0);
        close (fd[0]);
        close (fd2[0]);
        dup2(fd2[1],1);
        close (fd2[1]);
        arg2[0] = "grep";
        arg2[1] = "/bin/bash$";
        arg2[2] = NULL;
        execvp("grep", arg2);
        perror("execvp first child");
    }
}
else
{
    //i 'm the father
    close (fd[0]);
    dup2(fd[1],1);
    close (fd[1]);
    arg[0] = "cat";
    arg[1] = "/etc/passwd";
    arg[2] = NULL;
    execvp("cat", arg);
    perror("execvp father");    
}   

}

Upvotes: 1

Views: 12521

Answers (1)

Ben Jackson
Ben Jackson

Reputation: 93710

Your program very nearly works. What's missing is

    //i am child 2 (child of the child)
    close (fd[1]);
    close (fd[0]);

The pipe you called fd is for communicating between 'cat' and 'grep'. What's happening in your current code is that cat dumps the file and exits, closing its output. Grep reads all of that and waits for the EOF on its input. Since "child 2" still has the input side of the pipe open (it inherited it via fork), grep waits forever. If run your program and then type ps you should see a grep and a wc hanging around waiting to finish.

The other thing you would normally do when constructing a pipeline like this is arrange it so that the final task (in this case wc) is the one that the shell is waiting for. As written, when your program is run from the shell it will appear to finish when cat finishes, and the output of wc will print as if from a background task. If you arrange the pipe so that wc is under "i am child 1" then the shell will be waiting for wc instead.

Alternatively you could fork all of the three processes off and "child 1" would invoke wait() to wait for all of them before exiting. That waiting process would be like your own tiny shell.

Upvotes: 4

Related Questions