Leer datos de pipe en C

Jinku Hu 12 octubre 2023
  1. Utilice llamadas del sistema pipe y read para leer desde Pipe en C
  2. Utilice el bucle while para leer desde la pipe en C
Leer datos de pipe en C

Este artículo demostrará varios métodos sobre cómo leer desde una pipe en C.

Utilice llamadas del sistema pipe y read para leer desde Pipe en C

La pipe es una de las variantes de las primitivas de comunicación entre procesos (IPC) en los sistemas basados en UNIX. Proporciona un canal de comunicación unidireccional, es decir, un flujo de bytes entre dos procesos, y los datos se mueven secuencialmente en una dirección. La llamada al sistema pipe se utiliza para crear una pipe y adquirir descriptores de archivo para sus extremos de lectura y escritura. Tenga en cuenta que podemos operar en descriptores de pipe con las funciones habituales de E/S read y write. La llamada al sistema pipe toma un array int de dos elementos y la llamada exitosa devuelve dos descriptores de archivo que denotan el primer final de lectura y el segundo de escritura. Tenga en cuenta que los datos escritos en la pipe se almacenan en búfer en el kernel hasta que el lector recupera los bytes dados.

En el siguiente ejemplo, demostramos el uso básico de pipe para comunicarse entre los procesos padre e hijo. Al principio, creamos una pipe y almacenamos sus descriptores en el array pipe_fd. A continuación, llamamos fork en una expresión de declaración switch e incluimos el bloque de código del proceso hijo bajo el caso 0. El caso default, por otro lado, será ejecutado por el proceso padre.

Observe que el padre escribe la cadena que se ha recuperado del argumento de la línea de comando y luego espera a que el hijo termine. Mientras tanto, el proceso hijo lee de la pipe e imprime los bytes leídos en la consola. Tanto el hijo como el padre cierran un extremo de la pipe porque el proceso hijo hereda los descriptores de archivo del padre en fork. Finalmente, después de que se leen los bytes enviados, el hijo cierra el extremo leído de la pipe, terminando con una llamada de exit.

#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);
  }
}

Utilice el bucle while para leer desde la pipe en C

El código de muestra anterior tiene un error engañoso que puede producir una lectura parcial en el proceso hijo. La llamada read puede devolver menos datos que en la pipe, por lo que llamar a una llamada read en este ejemplo sería erróneo. Afortunadamente, la función read devuelve el número de bytes leídos y podemos implementar un bucle que agote los datos en la pipe, como se muestra en el siguiente ejemplo de código. Tenga en cuenta que las pipes tienen una capacidad fija y, si se alcanza el máximo, las operaciones de escritura se bloquearán hasta que el lector recupere algunos datos.

#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);
  }
}
Autor: 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

Artículo relacionado - C Pipe