Leia os dados do pipe em C

Jinku Hu 12 outubro 2023
  1. Use pipe e read Chamadas de sistema para ler a partir do pipe em C
  2. Use o loop while para ler o pipe em C
Leia os dados do pipe em C

Este artigo irá demonstrar vários métodos sobre como ler a partir de um pipe em C.

Use pipe e read Chamadas de sistema para ler a partir do pipe em C

O pipe é uma das variantes das primitivas de comunicação entre processos (IPC) em sistemas baseados em UNIX. Ele fornece um canal de comunicação unidirecional, ou seja, um fluxo de bytes entre dois processos, e os dados são movidos sequencialmente em uma direção. A chamada de sistema pipe é usada para criar um pipe e adquirir descritores de arquivo para suas extremidades de leitura e gravação. Observe que podemos operar em descritores de pipe com funções de E/S usuais read e write. A chamada do sistema pipe usa um array int de dois elementos e a chamada bem-sucedida retorna dois descritores de arquivo denotando o primeiro - leitura e o segundo - termina a escrita. Lembre-se de que os dados gravados no pipe são armazenados no kernel até que o leitor recupere os bytes fornecidos.

No exemplo a seguir, demonstramos o uso básico de pipe para a comunicação entre os processos pai e filho. Primeiramente, criamos um pipe e armazenamos seus descritores no array pipe_fd. Em seguida, chamamos fork em uma expressão de instrução switch e incluímos o bloco de código do processo filho sob o caso 0. O caso default, por outro lado, será executado pelo processo pai.

Observe que o pai escreve a string que foi recuperada do argumento da linha de comando e então espera que o filho termine. Enquanto isso, o processo filho lê a partir do canal e imprime os bytes lidos no console. O filho e o pai fecham a extremidade do pipe porque o processo filho herda os descritores de arquivo do pai em fork. Finalmente, depois que os bytes enviados são lidos, a extremidade de leitura do pipe é fechada pelo filho, terminando com uma chamada 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);
  }
}

Use o loop while para ler o pipe em C

O código de amostra anterior tem um bug enganoso que pode gerar leitura parcial no processo filho. A chamada read pode retornar menos dados do que no pipe, portanto, chamar uma chamada read neste exemplo seria incorreto. Felizmente, a função read retorna o número de bytes lidos e podemos implementar um loop que esgota os dados no pipe, conforme mostrado no próximo exemplo de código. Observe que os pipes têm capacidade fixa e, se o máximo for atingido, as operações de gravação serão bloqueadas até que o leitor recupere alguns dados.

#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