FIFO

パイプは名前を持たないため、共通の親プロセスを持つプロセス間でしか使用できない。FIFO(first in first out)はパイプと類似し、単方向(半二重)のデータの流れを提供する。
FIFOとパイプの違いは、FIFOがパス名と結合しており、互いに関係のないプロセスから、同一のFIFOにアクセスできること。
FIFOは名前付きパイプとも呼ばれることもある。

FIFOはmkfifo関数で作成する。

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

戻り値:成功なら0, エラーなら-1

FIFOは読み出し限定、あるいは書き込み限定でしかオープンできない。FIFOは半二重である。

パイプあるいはFIFOに対するlseekの呼び出しはESPIPEエラーを発生させる。


#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

#define FIFO_NAME "/tmp/fifo.tmp"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void
echo_server(int readfd)
{
        int n;
        char buff[1024];
        n = read(readfd, buff, sizeof(buff));
        buff[n] = '\0';
        printf("%s", buff);
}


void
echo_client(int writefd)
{
        char buff[1024];
        fgets(buff, sizeof(buff), stdin);
        write(writefd, buff, strlen(buff));
}


int
main()
{
        int readfd, writefd;
        pid_t childpid;
        mkfifo(FIFO_NAME, FILE_MODE);

        if ( (childpid = fork()) == 0 ) {//Child
                readfd = open(FIFO_NAME, O_RDONLY, 0);
                echo_server(readfd);
                exit(0);
        }

        //Parent
        writefd = open(FIFO_NAME, O_WRONLY, 0);

        echo_client(writefd);

        waitpid(childpid, NULL, 0);

        close(writefd);
       
        unlink(FIFO_NAME);

        return 0;
}


  • パイプの作成とオープンは、1回のpipeの呼び出しで同時に行える。FIFOの作成とオープンは、mkfifoの呼び出しに続けてopenの呼び出しを必要とする。

  • パイプは最後のクローズの後、自動的に消滅する。FIFOの名前をファイルシステムから消去するにはunlinkを呼び出す必要がある。






  • パイプとFIFOに関するそのほかの性質

  • ディスクリプタは2種類の方法で非ブロッキングにできる
  • 1.openの呼び出し時に、O_NONBLOCKフラグを指定する

  • writefd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0);


  • 2.ディスクリプタが既にオープンされている場合、fcntlを呼び出してO_NONBLOCKフラグを設定する

  • int flags;
    
    if ( (flags = fcntl(fd, F_GETFL, 0)) < 0 ) {
        perror("fcntl");
        exit(1);
    }
    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) < 0) {
        perror(fcntl);
        exit(1);
    }

  • パイプ、FIFOに現在格納されている寮より多いデータを読み取り要求した場合、その時点で格納されているデータのみが返される。要求した寮よりも少ない戻り値をreadが返すことを考慮すべき
  • writeに与えるバイト数が、PIPE_BUF以下である場合、そのWRITEはAtomicに実行される。writeに与えるバイト数がPIPE_BUFよりも大きかった場合、そのwriteがAtomicに実行される保証はない

  • クライアントFIFOをクライアントがオープンしていない場合に、サーバーがこのFIFOのopenの呼び出しでブロックする。

  • ストリームとメッセージ

  • ストリームI/Oは

  • レコード境界が存在しない

    読み書きの操作はデータ内容を全く検査しない

  • 転送されるデータに、アプリケーションが特定の構造を持たせたい場合

  • 1.帯域内に特別な終了シーケンス(CRLFなど)を埋め込む。終了シーケンス(デリミタ)をエスケープする必要あり。

    明示的なレコード長をレコード自身に含める

    パイプ、FIFOの読み書きに標準I/Oを用いることも出来る。ディスクリプタと結合している標準I/Oストリームをfdopen関数を用いて作成する。

    //メッセージを用いたエコーサーバ/クライアント
    
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    #include <limits.h> //PIPE_BUF
    
    #define MAXMESGDATA (PIPE_BUF - 2*sizeof(long))
    
    #define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATA)
    
    struct mymesg {
        long mesg_len;
        long mesg_type;
        char mesg_data[MAXMESGDATA];
    };
    
    ssize_t mesg_send(int, struct mymesg*);
    ssize_t mesg_recv(int, struct mymesg*);
    
    
    ssize_t
    mesg_send(int fd, struct mymesg *mptr)
    {
        return write(fd, mptr, MESGHDRSIZE + mptr->mesg_len);
    }
    
    ssize_t
    mesg_recv(int fd, struct mymesg *mptr)
    {
        size_t len;
        ssize_t n;
    
        if ( (n = read(fd, mptr, MESGHDRSIZE)) == 0 ) {
            return 0;//End of file
        } else if (n != MESGHDRSIZE) {
            perror("read");
            exit(1);
        }
        if ( (len = mptr->mesg_len) > 0 ) {
            if ( (n = read(fd, mptr->mesg_data, len)) != len ) {
                perror("read");
                exit(1);
            }
        }
        return len;
    }
    
    
    void
    echo_client(int writefd)
    {
        size_t len;
        ssize_t n;
        struct mymesg mesg;
    
        fgets(mesg.mesg_data, MAXMESGDATA, stdin);
        len = strlen(mesg.mesg_data);
        if (mesg.mesg_data[len-1] == '\n') {
            len--;
        }
        mesg.mesg_len = len;
        mesg.mesg_type = 1;
        mesg_send(writefd, &mesg);
    }
    
    
    void
    echo_server(int readfd)
    {
        FILE *fp;
        ssize_t n;
        struct mymesg mesg;
    
        mesg.mesg_type = 1;
        if ( (n = mesg_recv(readfd, &mesg)) == 0 ) {
            perror("mesg_recv");
            exit(1);
        }
        mesg.mesg_data[n] = '\0';
    
        printf("%s\n", mesg.mesg_data);
    }
    
    int
    main()
    {
        int pipe1[2];
        pid_t childpid;
    
        pipe(pipe1);
    
        if ( (childpid = fork()) == 0 ) {//Child
            close(pipe1[1]);
            echo_server(pipe1[0]);
            exit(1);
        }
    
        //Panret
        close(pipe1[0]);
    
        echo_client(pipe1[1]);
    
        waitpid(childpid, NULL, 0);
    
        return 0;
    }




    パイプとFIFOに関する量的な制限

    PIPE_BUFとOPEN_MAXの値を表示してみる。PIPE_BUFとOPEN_MAXは、に定義されている。

    #include <stdio.h>
    #include <limits.h>
    #include <unistd.h>
    
    int
    main()
    {
        printf("PIPE_BUF = %ld, OPEN_MAX = %ld\n",
               pathconf("/boot", _PC_PIPE_BUF), 
               sysconf(_SC_OPEN_MAX));
    
        return 0;
    }