IPC: パイプ

パイプは、二つのファイル記述子を用いた入出力メカニズムを作成します。

SYNOPSIS

#include <unistd.h>
 
int pipe(int fildes[2]);

DESCRIPTION

pipe()システムコールは、二つのファイル記述子を返します。これらのうち、一方はパイプへの書込み用で、他方は読み取り用に利用します。これは基本的な first-in-first-out(FIFO) の仕組みで、fildes[1] に書き込んだデータを fildes[0] から読み取ることが出来ます。

pipe

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)

 

戻る