How to Read Data From Pipe in C

Jinku Hu Feb 02, 2024
  1. Use pipe and read System Calls to Read From Pipe in C
  2. Use the while Loop to Read From Pipe in C
How to Read Data From Pipe in C

This article will demonstrate multiple methods about how to read from pipe in C.

Use pipe and read System Calls to Read From Pipe in C

The pipe is one of the variants of inter-process communication(IPC) primitives in UNIX-based systems. It provides a unidirectional communication channel, namely a byte stream between two processes, and the data is moved sequentially in one direction. pipe system call is used to create a pipe and acquire file descriptors for its read and write ends. Note that we can operate on pipe descriptors with usual I/O functions read and write. pipe system call takes two-element int array and the successful call returns two file descriptors denoting the first - read and the second - write ends. Mind that data written to the pipe is buffered in the kernel until the reader retrieves the given bytes.

In the following example, we demonstrate the basic usage of pipe to communicate between the parent and the child processes. At first, we create a pipe and store its descriptors in the pipe_fd array. Next, we call fork in a switch statement expression and include the child process’s code block under the case 0. The default case, on the other hand, will be executed by the parent process.

Notice that the parent writes the string that has been retrieved from the command line argument and then waits for the child to terminate. Meanwhile, the child process reads from the pipe and prints the read bytes to the console. Both child and parent close the one end of the pipe because the child process inherits the parent’s file descriptors on’ fork’. Finally, after the sent bytes are read, the read end of the pipe is closed by the child, terminating with an exit call.

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

enum { BUF_SIZE = 4096 };

int main(int argc, char *argv[]) {
  int pipe_fd[2];
  char buf[BUF_SIZE];
  ssize_t numRead;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s string\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  if (pipe(pipe_fd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  switch (fork()) {
    case -1:
      perror("fork");
      exit(EXIT_FAILURE);

    case 0:
      if (close(pipe_fd[1]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }

      sleep(3);
      numRead = read(pipe_fd[0], buf, BUF_SIZE);
      write(STDOUT_FILENO, buf, numRead);

      write(STDOUT_FILENO, "\n", 1);
      if (close(pipe_fd[0]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }
      _exit(EXIT_SUCCESS);

    default:
      if (close(pipe_fd[0]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }

      if (write(pipe_fd[1], argv[1], strlen(argv[1])) != strlen(argv[1])) {
        perror("write - partial/failed write");
        exit(EXIT_FAILURE);
      }

      if (close(pipe_fd[1]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }

      wait(NULL);
      exit(EXIT_SUCCESS);
  }
}

Use the while Loop to Read From Pipe in C

The previous sample code has one deceitful bug that can yield partial read in the child process. read call may return less data than in the pipe, so calling one read call in this example would be erroneous. Fortunately, the read function returns the number of read bytes, and we can implement a loop that exhausts the data in the pipe, as shown in the next code sample. Note that pipes have fixed capacity, and if the maximum is reached, the write operations will block until the reader retrieves some data.

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

enum { BUF_SIZE = 4096 };

int main(int argc, char *argv[]) {
  int pipe_fd[2];
  char buf[BUF_SIZE];
  ssize_t numRead;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s string\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  if (pipe(pipe_fd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }

  switch (fork()) {
    case -1:
      perror("fork");
      exit(EXIT_FAILURE);

    case 0:
      if (close(pipe_fd[1]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }

      while (1) {
        numRead = read(pipe_fd[0], buf, BUF_SIZE);
        if (numRead == -1) {
          perror("read");
          exit(EXIT_FAILURE);
        }
        if (numRead == 0) break;

        if (write(STDOUT_FILENO, buf, numRead) != numRead) {
          perror("write - partial/failed write");
          exit(EXIT_FAILURE);
        }
      }

      write(STDOUT_FILENO, "\n", 1);
      if (close(pipe_fd[0]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }
      _exit(EXIT_SUCCESS);

    default:
      if (close(pipe_fd[0]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }

      if (write(pipe_fd[1], argv[1], strlen(argv[1])) != strlen(argv[1])) {
        perror("write - partial/failed write");
        exit(EXIT_FAILURE);
      }

      if (close(pipe_fd[1]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }

      wait(NULL);
      exit(EXIT_SUCCESS);
  }
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C Pipe