從 C 語言中的管道讀取資料
Jinku Hu
2023年10月12日
本文將演示有關如何從 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 <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);
}
}