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; }
パイプとFIFOに関するそのほかの性質
writefd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0);
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); }
ストリームとメッセージ
レコード境界が存在しない
読み書きの操作はデータ内容を全く検査しない
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; }