從 C 語言中的管道讀取資料

Jinku Hu 2023年10月12日
  1. 使用 piperead 系統呼叫在 C 語言中從管道中讀取資料
  2. 在 C 語言中使用 while 迴圈從管道中讀取資料
從 C 語言中的管道讀取資料

本文將演示有關如何從 C 語言中的管道讀取的多種方法。

使用 piperead 系統呼叫在 C 語言中從管道中讀取資料

管道是基於 UNIX 的系統中的程序間通訊(IPC)原語的變體之一。它提供了一個單向通訊通道,即兩個程序之間的位元組流,並且資料在一個方向上順序移動。pipe 系統呼叫用於建立管道並獲取其讀取和寫入端的檔案描述符。注意,我們可以使用普通的 I/O 函式 readwrite 對管道描述符進行操作。pipe 系統呼叫採用包含兩個元素的 int 陣列,成功呼叫將返回兩個檔案描述符,分別表示第一個-讀取和第二個-寫入結束。請注意,寫入管道的資料會在核心中進行緩衝,直到讀取器檢索到給定的位元組為止。

在下面的示例中,我們演示了在父程序和子程序之間進行通訊的管道的基本用法。首先,我們建立一個管道並將其描述符儲存在 pipe_fd 陣列中。接下來,我們在 switch 語句表示式中呼叫 fork,並在情況 0 下包含子程序的程式碼塊。另一方面,default 情況將由父程序執行。

請注意,父級將寫入從命令列引數中檢索到的字串,然後等待子級終止。同時,子程序從管道讀取,並將讀取的位元組列印到控制檯。子程序和父程序都關閉管道的一端,因為子程序繼承了 fork 上父程序的檔案描述符。最後,在讀取傳送的位元組之後,子程序關閉管道的讀取端,並以 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);
  }
}

在 C 語言中使用 while 迴圈從管道中讀取資料

先前的示例程式碼有一個欺騙性的錯誤,該錯誤可能導致子程序中的部分讀取。read 呼叫返回的資料可能少於管道中的資料,因此在此示例中呼叫一個 read 呼叫將是錯誤的。幸運的是,read 函式返回讀取的位元組數,我們可以實現一個迴圈,以用盡管道中的資料,如下一個程式碼示例所示。請注意,管道具有固定的容量,如果達到最大值,則寫操作將阻塞,直到讀取器檢索到一些資料為止。

#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);
  }
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook