从 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