in C/C++, Linux

Introduction To Pipe In Multi-process

When we design a program with multi-processing,  we usually want processes to communicate with each other to finish a task. For example,in a program we may want process 1 to read a file from the system and do some operation, then send the the edited file directly to process 2 to do other operation.  

The technic we use here for inter-process communication is pipe. If we type "man pipe" in linux terminal, we can see its definition:

A unidirectional data channel that can be used for interprocess communication.
A pipe contains two ends: one for reading and the other one for writing

We can create pipe by using the following function in Linux:
int fd[2];
if (pipe(fd12) == 0)
      fprintf(stderr, "pipe create error");

we first declare a file descriptor array( integer type) with two elements and then send it to the function pipe(). The fd[0] will be assigned as the read end of the pipe and fd[1] will be assigned as the write end of the pipe.

When we call the pipe(fd[2]) function, the system will create a pipeline like this:

Then we use the fork() system call to create a process and the pipe line will be duplicated:

Notice that in the code blew, we close the reading or writing end in each process:


int fd[2];   // pipe endpoints
pid_t child_pid;

if (pipe(fd) == 0)
    fprintf(stderr, "pipe create error");

if ((child_pid = fork()) == 0)
    fprintf(stderr, "fork error");
else if (child_pid == 0) // this is the parent process( process 1)
    close(fd[0]); // close reading end of the pipe
    process_1(argc, argv, fd[1]); // write to fd[1]
if (waitpid(child_pid, NULL, 0) == 0) // wait for child
    err_sys("waitpid error"); }
else // this is the child process( process 2)
    close(fd[1]); // close writing end of the pipe
    process_2(argc, argv, fd[0]); // read from fd[0]

It is a good practice to close the unused ends after fork() and close all ends after read/write operation.If you do not close the pipe ends it may frozen your program in some situation. 
In our example, process 1 only use the write end to send data to process 2 and process 2 only use the read end to get data from process 1.So for a good programming style, we should close fd[0] in process 1 and close fd[1] in process 2.

The following code shows you the problem that do not close the close the pipe end:

void process_1(int argc, char *argv[], int fd)
  /* If you want to use read() and write(), this works:
   * write(fd, "hello world \n", 12);
FILE *fp = fdopen(fd, "w"); // use fp as if it had come from fopen()
FILE *file;
char *filename = "x.txt";
int temp_int;
int size = 0;

if (fp == NULL)
{ err_sys("fdopen(w) error"); }

//open file in read mode
file = fopen(filename, "r");

// The following is so we don't need to call fflush(fp) after each fprintf().
// The default for a pipe-opened stream is full buffering, so we switch to line
// buffering.
// But, we need to be careful not to exceed BUFFER_SIZE characters per output
// line, including the newline and null terminator.
static char buffer[BUFFER_SIZE]; // off the stack, always allocated
int ret = setvbuf(fp, buffer, _IOLBF, BUFFER_SIZE); // set fp to line-buffering
if (ret != 0)
{ err_sys("setvbuf error (parent)"); }

//get the char from file and write it to the pipe
while( (temp_int = fgetc(file)) != EOF ){
   fprintf((FILE*)fp, "%c", temp_int);
   size ++;
fprintf(stdout, "P1: file %s, bytes %d \n", filename, size);
void process_2(int argc, char *argv[], int fd)
  FILE *fp = fdopen(fd, "r");
if (fp == NULL)
{ err_sys("fdopen(r) error"); }

static char buffer[BUFFER_SIZE];
int ret = setvbuf(fp, buffer, _IOLBF, BUFFER_SIZE);
if (ret != 0)
{ err_sys("setvbuf error (child)"); }
//if not input any -m option

char line[BUFFER_SIZE];

while( fgets(line, BUFFER_SIZE,fp) != NULL){

fclose( fp);

When you run it, the program will get stuck at function fgets( line, BUFFER_SIZE,fp_r) because we do not close the writing end of the pipe in process 1 and fgets will go into a infinite loop to wait the EOF. 

The solution is quiet simple: just adding the following line at the end of process 1:

fclose(fp); //close the end point

Write a Comment