IPC: パイプ
パイプは、二つのファイル記述子を用いた入出力メカニズムを作成します。
SYNOPSIS
#include <unistd.h> int pipe(int fildes[2]);
DESCRIPTION
pipe()
システムコールは、二つのファイル記述子を返します。これらのうち、一方はパイプへの書込み用で、他方は読み取り用に利用します。これは基本的な first-in-first-out(FIFO)
の仕組みで、fildes[1]
に書き込んだデータを fildes[0]
から読み取ることが出来ます。
SAMPLE
パイプに対して書き込んだ内容を読み込みます。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main() { int fildes[2]; char buf[30]; if (pipe(fildes) == -1) { perror("pipe"); exit(1); } printf("file descriptor for writing: #%d\n", fildes[1]); printf("file descriptor for reading: #%d\n", fildes[0]); write(fildes[1], "hello", 5); read(fildes[0], buf, 5); printf("read from pipe: \"%s\"\n", buf); return 0; }
これをコンパイルし実行すると以下のようになります。
# gcc pipe_sample.c -o pipe_sample # ./pipe_sample file descriptor for writing: #4 file descriptor for reading: #3 read from pipe: "hello" #
しかし、このような同一プロセス内ではパイプを使うメリットがありません。パイプを使うのは、他のプロセスとやり取りが必要なときにメリットが生じます。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main() { int fildes[2]; char buf[30]; if (pipe(fildes) == -1) { perror("pipe"); exit(1); } switch(fork()) { case -1: perror("fork"); exit(1); case 0: printf(" CHILD: writing to the pipe\n"); write(fildes[1], "hello", 5); printf(" CHILD: exiting...\n"); exit(0); default: printf("PARENT: reading from pipe\n"); read(fildes[0], buf, 5); printf("PARENT: read \"%s\"\n", buf); wait(NULL); } return 0; }
これをコンパイルし実行すると以下のようになります。
# gcc pipe_sample2.c -o pipe_sample2 # ./pipe_sample2 CHILD: writing to the pipe CHILD: exiting... PARENT: reading from pipe PARENT: read "hello" #
コマンドライン上で、"ls | wc -l"
という具合に標準入出力をパイプでつないだ処理をプログラムで表現してましょう。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main() { int fildes[2]; char buf[30]; if (pipe(fildes) == -1) { perror("pipe"); exit(1); } switch(fork()) { case -1: perror("fork"); exit(1); case 0: dup2(fildes[1], 1); /* make stdout same as fildes[1] */ close(fildes[0]); /* not need */ execl("/usr/bin/ls", "ls", NULL); exit(0); default: dup2(fildes[0], 0); /* make stdin same as fildes[0] */ close(fildes[1]); /* not need */ execl("/usr/bin/wc", "wc", "-l", NULL); } return 0; }
これをコンパイルし実行すると以下のようになります。
# gcc pipe_sample3.c -o pipe_sample3 # ./pipe_sample3 6 #
このように、pipe()
システムコールを使うことで二つのプロセス(パイプをもっと開けば複数のプロセス)での入出力の処理を制御することが出来ます。
もし、ある一つのプロセスの入出力を制御したい場合、popen()
関数を利用できます。
#include <stdio.h> int main() { char str[BUFSIZ], *ptr; FILE *fp; int lines=0; if ((fp=popen("ls","r")) == NULL) { perror("popen"); exit(1); } while(1) { fgets(str,BUFSIZ,fp); if (feof(fp)) { break; } lines++; printf("%d: %s", lines, str); } pclose(fp); return 0; }
SEE ALSO
pipe(2), dup(2), dup2(3), popen(3), pclose(3)