How to use pipes in c++

Today we are going to see, how can we use pipes and fork system call for creating multiple processes and handling our work. We are going to implement a use case that will use a fork system call to generate two processes, one will make use of ls command and pass its output to the second child which will display the output of ls with numbering. I am assuming most of you are familiar with at least the basics of C++. If not, I would highly recommend learning C++ before moving on to these advanced concepts. Let us start without any delay by learning more about pipes in c++.

Fork

The fork() system call is to start a new process, known as a child process. It runs in parallel with the one that made the fork() call (parent process). After the fork() system call creates a new child process, both processes will execute the next instruction. A child process uses the same pc (program counter), CPU registers, and open files as the parent process does. It accepts no input and returns an integer value. The values returned by fork are (-ive value, 0, +ive value). A negative Value means the process of creating a child was unsuccessful. Returning to the freshly generated child process at zero. Return to parent or caller if the value is positive.

Pipes

A pipe is a connection between two processes that allows the standard output of one process to become the standard input of the other. Pipes are in the UNIX Operating System to communicate between related processes (inter-process communication). Pipes are only capable of one-way communication, which means that one process can write to the pipe while another reads from it. It creates a pipe, which is a section of the main memory that acts as a “virtual file.”

  • The pipes are for reading and writing by the generating process and all its child processes. This “virtual file” or conduit can be written to by one process and read from by another.
  • When a process tries to read before writing to the pipe, the operation is halted until the pipe is written to.
  • The pipe system call allocates the read and writes ends of the pipe to the first two accessible slots in the process’s open file table.

Project Setup:

  • Open a terminal and create a new file main.c
  • Put the following code inside it.
//Imports
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>

int main()
{
    int fd[2];//creating an integer array of file descriptors
    int cP1_id, cP2_id; //child processes ids
    char *argv[5]; //arguments list for execvp

    pipe(fd); //opening pipe

    // Create the first child and run the first command (ls -F).
    cP1_id = fork();
    if (cP1_id<0) {
        perror("Failed to create a child(1) process");
        return -1;
    }
    if (cP1_id == 0) {
        /* Now we will Set the process output to the input of the pipe.
            so that when we will run nl command it will become input of it
         */
        close(1); //we are closing STDOUTPUT
        dup(fd[1]);//duplicating Write fd[1] corresponds to write
        close(fd[0]); //closing read (fd[0] corresponds to read)
        close(fd[1]);//closing fd[1] write

        //lets now set arguments
        argv[0] = (char*) malloc(3*sizeof(char));
        argv[1] = (char*) malloc(3*sizeof(char));

        strcpy(argv[0],"ls");
        strcpy(argv[1],"-F");
        /*setting NULL at index 2, it basically corresponds
        to end of arguments */
        argv[2] = NULL;

        //calling execvp now
        int statusExec = execvp(argv[0],argv);
        if(statusExec < 0){
            printf("Failed to run execvp call\n");
            return -1;
        }

    }
    /* Wait for the children to finish, then exit.
 Other wise if main exit before them\n they become orphan processes*/
    wait(NULL);
    /* Now let's Create the second child and run the nl command. */
    cP2_id = fork();
    if (cP2_id<0) {
        printf("Failed to create a child(2) process\n");
        return -1;
    }
    if (cP2_id==0) {
        /* Now we are setting output we had as input for nl command. */
        close(0); //first lets close STDIN
        dup(fd[0]); //lets duplicate stdin
        close(fd[0]); //closing fd[0] ->read
        close(fd[1]); //closing fd[1] ->write

        //setting arguments for nl command
        argv[0] = (char*) malloc(3*sizeof(char));
        strcpy(argv[0],"nl");
        argv[1] = NULL;

        //calling execvp
        int statusExec = execvp(argv[0],argv);

        if(statusExec < 0){
            printf("Failed to run execvp call\n");
            return -1;
        }
    }

    close(fd[0]); //closing fd[0] ->read
    close(fd[1]); //closing fd[1] ->write
    /* Wait for the children to finish, then exit.
Other wise if main exit before them\n they become orphan processes*/
    wait(NULL);
    return 0;
}
  • Save the file and again open terminal
  • Run the file using the command: gcc main.c -o main -Wall and then ./main. Running the command requires installation of c++ compiler that can be downloaded from here.

Output

First, it lists all the directories and files where main.c is placed and then passes it to second child which displays them orderly.

Conclusion

In this article, we learnt about pipes in c++ which is an essential concept in systems programming. We have other c++ articles that can be checked from here.

Scroll to Top