FIFO管道
Posted tianzeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FIFO管道相关的知识,希望对你有一定的参考价值。
为了解决非亲属进程间通信这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)
FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道
#include < sys/types.h > #include < sys/stat.h > int mknod(const char *path,mode_t mod,dev_t dev); int mkfifo(const char *path,mode_t mode);
函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。这两个函数调用成功都返回0,失败都返回-1。
之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在)来为管道命名。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。当删除FIFO文件时,管道连接也随之消失
0x1: 有名管道的操作规则
1. 有名管道的打开规则
1. 如果当前打开操作是为读而打开FIFO时 1) 若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回 2) 否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志) 3) 或者,成功返回(当前打开操作没有设置阻塞标志) 2. 如果当前打开操作是为写而打开FIFO时 1) 如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回 2) 否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志) 3) 或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)
2. 有名管道的读写规则
1. 从FIFO中读取数据: 如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。 1) 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试 2) 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种,解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量 2.1) 当前FIFO内有数据,但有其它进程在读这些数据 2.2) 另外就是FIFO内没有数据 3) 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0) 4) 如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞 2. 向FIFO中写入数据: 如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作 1) 对于设置了阻塞标志的写操作 1.1) 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作 1.2) 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回 2) 对于没有设置阻塞标志的写操作 2.1) 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回 2.2) 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写
3.有名管道的使用
使用命名管道时,必须先调用open()将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。需要注意的是,调用open()打开命名管道的进程可能会被阻塞。但如果同时用读写方式(O_RDWR)打开,
则一定不会导致阻塞;如果以只读方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写方打开管道;同样以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。
实例(FIFO应用于父子进程间)
//fifo.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include "unpipc.h" #include "fifo.h" #include "client.h" #include "server.h" #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" int main() { int readfd,writefd; pid_t cpid; if((mkfifo(FIFO1,FILE_MODE)<0)&&(errno!=EEXIST)) err_sys("can‘t create %s",FIFO1); if((mkfifo(FIFO2,FILE_MODE)<0)&&(errno!=EEXIST)) { unlink(FIFO1); err_sys("can‘t create %s",FIFO1); } if((cpid=fork())==0) { readfd=open(FIFO1,O_RDONLY,0); writefd=open(FIFO2,O_WRONLY,0); server(readfd,writefd); exit(0); } writefd=open(FIFO1,O_WRONLY,0); readfd=open(FIFO2,O_RDONLY,0); client(readfd,writefd); waitpid(cpid,NULL,0); close(readfd); close(writefd); unlink(FIFO1); unlink(FIFO2); exit(0); }
//client.h #include <string.h> #include "unpipc.h" //#include "my_err.h" #define MAXLINE 4096 void client(int readfd,int writefd) { char buff[MAXLINE]; fgets(buff,MAXLINE,stdin); size_t len=strlen(buff); if(buff[len-1]==‘\n‘) len--; write(writefd,buff,len); ssize_t n; while((n=read(readfd,buff,MAXLINE))>0) write(STDOUT_FILENO,buff,n); }
//server.h #include "unpipc.h" #include "my_err.h" #include <string.h> void server(int readfd,int writefd) { int fd; ssize_t n; char buff[MAXLINE+1]; if((n=read(readfd,buff,MAXLINE))==0) err_quit("end-of-file while reading pathname"); buff[n]=‘\0‘; if((fd=open(buff,O_RDONLY))<0) { snprintf(buff+n,sizeof(buff)-n,":can‘t open, %s\n",strerror(errno)); n=strlen(buff); write(writefd,buff,n); } else { while((n=read(fd,buff,MAXLINE))>0) write(writefd,buff,n); close(fd); } }
FIFO应用于无亲属关系的进程间:
//client.c
#include <sys/types.h> #include <sys/stat.h> #include "fifo.h" #include "client.h" int main() { int readfd,writefd; writefd=open(FIFO1,O_WRONLY,0); readfd=open(FIFO2,O_RDONLY,0); client(readfd,writefd); close(readfd); close(writefd); unlink(FIFO1); unlink(FIFO2); exit(0); }
//server.c
#include <sys/types.h> #include <sys/stat.h> #include "fifo.h" #include "unpipc.h" #include "server.h" int main() { int readfd,writefd; if((mkfifo(FIFO1,FILE_MODE)<0)&&(errno!=EEXIST)) err_sys("can‘t create %s",FIFO1); if((mkfifo(FIFO2,FILE_MODE)<0)&&(errno!=EEXIST)) { unlink(FIFO1); err_sys("can‘t create %s",FIFO2); } readfd=open(FIFO1,O_RDONLY,0); writefd=open(FIFO2,O_WRONLY,0); server(readfd,writefd); exit(0); }
//fifo.h
#include "unpipc.h" #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2"
总结:pipe与FIFO间的区别
1、对文件系统来说,匿名管道是不可见的,它的作用仅限于在父进程和子进程两个进程间进行通信。而命名管道是一个可见的文件
2、pipe只能是两个具有公共祖先的进程间进行通信,而FIFO可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。
3、pipe创建出来的管道没有名字,而FIFO是通过唯一路径来标识唯一的命名管道。
以上是关于FIFO管道的主要内容,如果未能解决你的问题,请参考以下文章