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

Jinku Hu 2023年1月30日 2021年3月21日
  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 <unistd.h>
#include <sys/wait.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 <unistd.h>
#include <sys/wait.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