C 言語でパイプからデータを読み取る
この記事では、C 言語でパイプから読み取る方法に関する複数の方法を示します。
pipe
および read
システムコールを使用して、C のパイプから読み取る
パイプは、UNIX ベースのシステムにおけるプロセス間通信(IPC)プリミティブの変形の 1つです。これは、単方向通信チャネル、つまり 2つのプロセス間のバイトストリームを提供し、データは一方向に順次移動されます。pipe
システムコールは、パイプを作成し、その読み取りおよび書き込み終了用のファイル記述子を取得するために使用されます。通常の I/O 関数読み取り
および書き込み
を使用してパイプ記述子を操作できることに注意してください。pipe
システムコールは 2 要素の int
配列を取り、呼び出しが成功すると、最初の読み取りと 2 番目の書き込みの終了を示す 2つのファイル記述子が返されます。パイプに書き込まれたデータは、リーダーが指定されたバイトを取得するまでカーネルにバッファリングされることに注意してください。
次の例では、親プロセスと子プロセスの間で通信するためのパイプの基本的な使用法を示します。最初に、パイプを作成し、その記述子を 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);
}
}
while
ループを使用して C のパイプから読み取る
前のサンプルコードには、子プロセスで部分的な読み取りが発生する可能性のある 1つの欺瞞的なバグがあります。read
呼び出しはパイプ内よりも少ないデータを返す可能性があるため、この例で 1つの 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);
}
}