Fork, Exec, and Pipe in C | Learning Programming

Let's start by creating a new pipe using pipe


int pipefd[2];
pipe(pipefd);

pipe takes 1 argument, an integer array with a size of 2. This creates a read and a write pipe 0 being read, and 1 being write. Now we want to fork, so we have a child and a parent. The parent in this case is the real program, and the child is just temporarily there so we can call execlp. Speaking of which let's do that


if(fork() == 0) 
{
  close(pipefd[0]); // close read
  dup2(pipefd[1], 1); // set stdout to write
  //dup2(pipefd[1], 2); // set stderr to write
  close(pipefd[1]); // we are done modifying it so close it
  execlp("ls", "ls", NULL); // call command
}

To start we close the reading part of the pipe because we won't be using it in the child. Then we use dup2 which duplicates a file descriptor. We put pipefd[1], and 1 because we want our output to be output to the pipe. Then we close the write part of the pipe. Now we can finally call execlp. This takes a command as the first argument and then lists the command again and then all it's arguments as the rest of the arguments, and it always ends with NULL so the function can tell we are done passing arguments to it! So if we wanted to use the -l flag on ls we would do this:


execlp("ls", "ls", "-l", NULL); // call command

If there are no arguments just use NULL as in the first example. Now let's handle the parent's side of things.


else
{
  // parent
  char *bigbuf = calloc(100000, sizeof(char)); // bigger buff
  char *buffer= calloc(1000, sizeof(char)); // buffer
  close(pipefd[1]); // close write, don't need it
  while(read(pipefd[0], buffer, 1000) != 0)
  {
    strncpy(bigbuf, buffer, strlen(buffer));
  }
  printf("%s", bigbuf);
  close(pipefd[0]); // we are done with this pipe
}

First we create two buffers one that holds all the string information we are going to print at the end, we will call this string bigbuf. Second we create a buffer for each time we read a chunk of information from the child's stdout which we will call buffer. We can close the write part of the pipe because it's already setup because we did that in the child. Then we loop through using read to read all the information from the pipe file descriptors. We use pipefd[0] because it is the reading side of the pipe. We copy the characters from buffer to bigbuf, and then after the loop ends we just print bigbuf. Once that's all done we can close the other part of the pipe and we are done! Here is the source code:


#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int pipefd[2];
    pipe(pipefd);

    if(fork() == 0)
    {
        // child
        close(pipefd[0]); // close read

        dup2(pipefd[1], 1); // set stdout to write
        //dup2(pipefd[1], 2); // set stderr to write

        close(pipefd[1]); // we are done modifying it so close it
        execlp("ls", "ls", "-l", NULL); // call command
    }
    else
    {
        // parent
        char *bigbuf = calloc(100000, sizeof(char)); // bigger buff
        char *buffer= calloc(1000, sizeof(char)); // buffer

        close(pipefd[1]); // close write don't need it

        while(read(pipefd[0], buffer, 1000) != 0)
        {
            strncpy(bigbuf, buffer, strlen(buffer));
        }
        printf("%s", bigbuf);

        close(pipefd[0]); // we are done with this pipe
    }
    return 0;
}

Tags
C
Programming
Teaching
Linux
Learning
Fork
Exec
Pipe
Tutorial
Learn