Open In App

Demostrating Bidirectional Communication Using Ordinary Pipe

Last Updated : 24 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

An ordinary pipe is one of the methods Inter-Process Communication(IPC). Ordinary pipe allows two processes to communicate with each other (mainly parent and child) in which one process writes from one end and the other reads from the other end, resulting in unidirectional communication. They are a lightweight and efficient way to communicate between processes and are also known as anonymous pipes or unnamed pipes. They are volatile and are destroyed when either of the two processes (ie child or parent) that created them terminates.

Why Do We Need Ordinary Pipes?

When we make a child of a process using fork(), the address space of the two processes becomes different. One way to communicate between parent and child is piping. Ordinary pipes are used in inter-process communication (IPC) because they are easy to create, efficient in transferring of data, and flexible. When processes using these pipes terminate or finish their communication, the operating system automatically releases and deallocates the resources associated with these pipes. This includes the memory used for buffering data and the data structures used for managing the pipe. Because of this automatic cleanup and resource deallocation, ordinary pipes are considered more resource-efficient compared to other inter-process communication (IPC) mechanisms that may require manual resource management.

How Does Ordinary Pipe Work?

In Unix system, ordinary pipes are constructed using pipe().

The parent function creates the pipe using pipe() . After creating the pipe it calls fork() to create a child process. Depending on the implementation of the child and parent process, they communicate with each other.

Approach
Create 2 pipes.
In pipe1 child reads and the parent writes.
In pipe2 child writes and the parent reads.
We read from 0th end of a pipe (ie pipe1[0] or pipe2[1]) and write on 1th end of a pipe(ie pipe1[1] and pipe2[1]) .
PIPE

PIPE DIAGRAM

Important fuctions used in the code.

FUNCTION

LIBRARY

DISCRIPTION

pipe(pipe1)

unistd.h

Creates a pipe for interprocess communication.

fork()

unistd.h

Creates a pipe for interprocess communication.

close(pipe1[0])

unistd.h

Closes a pipe1[0].

write(pipe1[1], buf, size)

unistd.h

Writes data from a buffer to pipe1[1]

read(pipe1[0], buf, size)

unistd.h

Reads data from pipe1[0] into a buffer.

perror(msg)

stdio.h

Prints an error message to the standard error stream with a custom message.

exit(status)

stdlib.h

Terminates the current process with an exit status.

Implementation of Bidirectional Communication Between Child and Parent Processes Using Ordinary Pipe

Below is the UNIX C program to implement pipe.

C




#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
  
int main() {
  // Declare two pipe arrays
  int pipe1[2], pipe2[2];
  
  // Declare two message strings
  char *m1 = "gfg";
  char *m2 = "GKG";
  
  // Declare a buffer to store the received message
  char m_store[100];
  
  // Create the first pipe
  if (pipe(pipe1) == -1) {
    perror("cannot create pipe 1");
    exit(1);
  }
  
  // Create the second pipe
  if (pipe(pipe2) == -1) {
    perror("cannot create pipe 2");
    exit(1);
  }
  
  // Fork a child process
  int p;
  p = fork();
  
  // If the fork failed
  if (p < 0) {
    perror("child fail");
    exit(1);
  }
  
  // If this is the child process
  else if (p == 0) {
    // Close the write end of the first pipe
    close(pipe1[1]);
  
    // Close the read end of the second pipe
    close(pipe2[0]);
  
    // Write the message "gfg" to the second pipe
    write(pipe2[1], m1, sizeof(m1));
  
    // Read a message from the first pipe
    read(pipe1[0], m_store, sizeof(m_store));
  
    // Print the received message
    printf("Child reads %s \n", m_store);
  
    // Close the read end of the first pipe
    close(pipe1[0]);
  
    // Close the write end of the second pipe
    close(pipe2[1]);
  }
  
  // If this is the parent process
  else {
    // Close the write end of the second pipe
    close(pipe2[1]);
  
    // Close the read end of the first pipe
    close(pipe1[0]);
  
    // Write the message "GKG" to the first pipe
    write(pipe1[1], m2, sizeof(m2));
  
    // Read a message from the second pipe
    read(pipe2[0], m_store, sizeof(m_store));
  
    // Print the received message
    printf("Parent reads %s \n", m_store);
  
    // Close the read end of the second pipe
    close(pipe2[0]);
  
    // Close the write end of the first pipe
    close(pipe1[1]);
  }
  
  return 0;
}


Output

Parent reads gfg 
Child reads GKG 





Conclusion

By using two ordinary pipes in two-way communication is beneficial because it is simpler to implement and efficient.However, there are some drawbacks to using two ordinary pipes in two-way communication.

  • It can be difficult to manage the two pipes, especially if there is a lot of data being exchanged.
  • Pipes can be blocked if one process tries to read from an empty pipe or write to a full pipe.

To overcome these drawbacks, named pipes can be used instead of ordinary pipes for two-way communication. Named pipes are named objects that can be used by multiple processes. They also have buffering capabilities, which can help to prevent blocking.

Frequently Asked Questions

1. What are the limitation of the ordinary pipe?

Some limitations of ordinary pipes are :

  • One way communication.
  • Relationship must exist ( parent-child).
  • Cannot be accessed from outside the process that created it.

These all can be resolved using named pipe.

2. What is named pipe?

A named pipe (also known as a FIFO) is one of the methods for inter-process communication. It is an extension to the traditional pipe (ordinary pipe) concept on Unix.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads