Multiple Pipes in C

Waqar Aslam Oct 12, 2023
  1. Overview of Pipes in C
  2. Implement Multiple Pipes in C
Multiple Pipes in C

This article demonstrates the implementation of multiple pipes in C.

Overview of Pipes in C

A pipe is an abstract representation of a link between two different processes in which the standard output returned from one process becomes the standard input of the other. Pipes are an important component of the UNIX operating system that enables linked processes to communicate with one another (inter-process communication).

Pipes can only communicate in one direction; thus, we may utilize them to have one process write to the pipe while another reads from the pipe. Pipes are areas of main memory that are handled in the same manner as virtual files, and this action opens one.

Implement Multiple Pipes in C

Let us look at an example with 1 parent and 2 child processes. We send a value guaranteed to be complete from the parent process to the child process via pipes, and then we send that value back to the parent process with some increment added.

To begin, we will need to import the libraries to access the classes and functions included in our application. In addition, a main() function should be developed.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char* argv[]) {}

Inside the main function, we will generate file descriptors for three pipes so that we can read and write on other processes. Then, you will need to define a variable with the type i to be an integer to establish a for loop that will generate three pipes and check that the value of all of the pipes is less than zero to return an error.

int fd[3][2];
int i;
for (i = 0; i < 3; i++) {
  if (pipe(fd[i]) < 0) {
    return 1;
  }
}

After we have finished opening the pipes, the following step will be to begin to fork each process. As a result, you should establish a variable called processIdOne and then assign the fork() function.

Also, apply a check to verify that the value of processIdOne is not less than zero.

int processIdOne = fork();
if (processIdOne < 0) {
  return 2;
}

Now, we check to see whether the value of processIdOne is equal to zero; if it is, this indicates that we are in the first child process. The close() function has to be used inside the first child process to shut all file descriptors except for the one belonging to this process.

Create an integer variable with the name x and give it the datatype int. Now, we use the read() method, provide it the file descriptor, &x, and the size to read the value, and check to see whether its value is less than zero before displaying an error message if it is.

Next, we check to see whether the fileDescriptor[1][1] value is less than zero and display an error if it is. We do this by utilizing the write() method, passing it its parameters, and writing using the fileDescriptor[1][1].

The file descriptors for the first child process should be closed in the last step.

if (processIdOne == 0) {
  // Child process 1
  close(fileDescriptor[0][1]);
  close(fileDescriptor[1][0]);
  close(fileDescriptor[2][0]);
  close(fileDescriptor[2][1]);
  int x;
  if (read(fileDescriptor[0][0], &x, sizeof(int)) < 0) {
    return 3;
  }
  x += 10;
  if (write(fileDescriptor[1][1], &x, sizeof(int)) < 0) {
    return 4;
  }
  close(fileDescriptor[0][0]);
  close(fileDescriptor[1][1]);
  return 0;
}

The next step is establishing a variable called processIdTwo and then assigning the fork() function. In addition, a check should be performed to ensure that the value of processIdTwo is not equal to or lower than zero.

int processIdTwo = fork();
if (processIdTwo < 0) {
  return 5;
}

Now, we check to see whether the processIdTwo has the value zero; if it does, it indicates that we are in the second child process. Within the second child process, we need to ensure that all file descriptors are closed using the close() method except for this particular process.

Declare a variable with the datatype int called x. Now, we use the read() method, feed it the file descriptor, &x, and the size to read the value, and ensure that an error is shown if the value is less than zero.

Next, the value of x is increased by adding the value 10 to it, and we use the write() function with its parameters to write using the fileDescriptor[2][1]. We then check to see whether its value is less than zero and if it is, we display an error message.

Lastly, close the file descriptors of the second child process.

if (processIdTwo == 0) {
  close(fileDescriptor[0][0]);
  close(fileDescriptor[0][1]);
  close(fileDescriptor[1][1]);
  close(fileDescriptor[2][0]);
  int x;
  if (read(fileDescriptor[1][0], &x, sizeof(int)) < 0) {
    return 6;
  }
  x += 10;
  if (write(fileDescriptor[2][1], &x, sizeof(int)) < 0) {
    return 7;
  }
  close(fileDescriptor[1][0]);
  close(fileDescriptor[2][1]);
  return 0;
}

As we have done before, we will now shut all the other file descriptors in the parent process except for the one corresponding to this process.

Declare the variable x to be of the type integer. Now, we use the read() function, give it the file descriptor, &x, and the size to read the value, and check to see whether its value is less than zero to determine whether an error should be shown.

Next, the value of x is increased by adding the value 10. We use the write() function with its parameters to write using the fileDescriptor[2][0], and we check to see whether its value is less than zero to determine whether or not to display an error message.

Put a stop to the open file descriptors by closing them.

For both processIdOne and processIdTwo, the waitpid() method should be used. This function waits for all the processes to be complete before moving on to the next operation.

close(fileDescriptor[0][0]);
close(fileDescriptor[1][0]);
close(fileDescriptor[1][1]);
close(fileDescriptor[2][1]);
int x = 0;
if (write(fileDescriptor[0][1], &x, sizeof(int)) < 0) {
  return 8;
}
if (read(fileDescriptor[2][0], &x, sizeof(int)) < 0) {
  return 9;
}
printf("The value received from the child processes is: %d\n", x);
close(fileDescriptor[0][1]);
close(fileDescriptor[2][0]);

waitpid(processIdOne, NULL, 0);
waitpid(processIdTwo, NULL, 0);

Complete Source Code to Implement Multiple Pipes in C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
  int fileDescriptor[3][2];
  int i;
  for (i = 0; i < 3; i++) {
    if (pipe(fileDescriptor[i]) < 0) {
      return 1;
    }
  }

  int processIdOne = fork();
  if (processIdOne < 0) {
    return 2;
  }

  if (processIdOne == 0) {
    // Child process 1
    close(fileDescriptor[0][1]);
    close(fileDescriptor[1][0]);
    close(fileDescriptor[2][0]);
    close(fileDescriptor[2][1]);
    int x;
    if (read(fileDescriptor[0][0], &x, sizeof(int)) < 0) {
      return 3;
    }
    x += 10;
    if (write(fileDescriptor[1][1], &x, sizeof(int)) < 0) {
      return 4;
    }
    close(fileDescriptor[0][0]);
    close(fileDescriptor[1][1]);
    return 0;
  }

  int processIdTwo = fork();
  if (processIdTwo < 0) {
    return 5;
  }

  if (processIdTwo == 0) {
    // Child process 2
    close(fileDescriptor[0][0]);
    close(fileDescriptor[0][1]);
    close(fileDescriptor[1][1]);
    close(fileDescriptor[2][0]);
    int x;
    if (read(fileDescriptor[1][0], &x, sizeof(int)) < 0) {
      return 6;
    }
    x += 10;
    if (write(fileDescriptor[2][1], &x, sizeof(int)) < 0) {
      return 7;
    }
    close(fileDescriptor[1][0]);
    close(fileDescriptor[2][1]);
    return 0;
  }

  // Parent process
  close(fileDescriptor[0][0]);
  close(fileDescriptor[1][0]);
  close(fileDescriptor[1][1]);
  close(fileDescriptor[2][1]);
  int x = 0;
  if (write(fileDescriptor[0][1], &x, sizeof(int)) < 0) {
    return 8;
  }
  if (read(fileDescriptor[2][0], &x, sizeof(int)) < 0) {
    return 9;
  }
  printf("The value received from the child processes is: %d\n", x);
  close(fileDescriptor[0][1]);
  close(fileDescriptor[2][0]);

  waitpid(processIdOne, NULL, 0);
  waitpid(processIdTwo, NULL, 0);

  return 0;
}

Output:

The value received from the child processes is: 20
Author: Waqar Aslam
Waqar Aslam avatar Waqar Aslam avatar

I am Waqar having 5+ years of software engineering experience. I have been in the industry as a javascript web and mobile developer for 3 years working with multiple frameworks such as nodejs, react js, react native, Ionic, and angular js. After which I Switched to flutter mobile development. I have 2 years of experience building android and ios apps with flutter. For the backend, I have experience with rest APIs, Aws, and firebase. I have also written articles related to problem-solving and best practices in C, C++, Javascript, C#, and power shell.

LinkedIn

Related Article - C Pipe