从 C 语言中的管道读取数据
本文将演示有关如何从 C 语言中的管道读取的多种方法。
使用 pipe
和 read
系统调用在 C 语言中从管道中读取数据
管道是基于 UNIX 的系统中的进程间通信(IPC)原语的变体之一。它提供了一个单向通信通道,即两个进程之间的字节流,并且数据在一个方向上顺序移动。pipe
系统调用用于创建管道并获取其读取和写入端的文件描述符。注意,我们可以使用普通的 I/O 函数 read
和 write
对管道描述符进行操作。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);
}
}
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