基本概念
FIFO指先进先出(first in,first out),它是一个单向(半双工)数据流。不同于管道的是,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO, 不相关的进程也能交换数据。FIFO也称为有名管道(named pipe), 以区分管道(pipe)。
FIFO是Linux基础文件类型中的一种(是一种伪文件),以伪文件形式存在于文件系统中,但FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道(可以理解为内核中的一块内存吧)。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。有名管道的名字存在于文件系统中,内容存放在内存中。
创建方式:
1. 命令:mkfifo 管道名
2. 库函数:int mkfifo(const char *pathname, mode_t mode); 成功:0; 失败:-1
其中pathname是一个普通的路径名,它是该FIFO的名字。mode参数指定文件权限位,类似于open的第三个参数。
mkfifo函数已隐含指定O_CREAT | O_EXCL。也就是说,它要么创建一个新的FIFO,要么返回一个EEXIST错误(如果所指定名字的FIFO已经存在)。要打开一个已存在的FIFO或创建一个新的FIFO,应先调用mkfifo,再检查它是否返回EEXIST错误,若返回该错误则改为调用open。
一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可用于fifo。如:close、read、write、unlink等。
fifo 实例
fifoR.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/.fifo"
int main()
{
int rst, rfd;
char buffer[1024];
rst = mkfifo(FIFO_PATH, 0666);
do {
if (rst == -1 && errno == EEXIST) {
if (errno == EEXIST) {
perror("the fifo file is exist:");
break;
}
perror("mkfifo failed:");
return -1;
}
}while(0);
rst = open(FIFO_PATH, O_RDWR);
if (rst == -1) {
perror("open failed:");
return -1;
} else {
rfd = rst;
}
while (1) {
memset(buffer, 0, 1024);
read(rfd, buffer, 1024);
printf("---------%s-------\n", buffer);
sleep(5);
}
return 0;
}
fifoW.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/.fifo"
int main()
{
int rst, i, wfd;
char buffer[1024];
rst = mkfifo(FIFO_PATH, 0666);
do {
if (rst == -1 && errno == EEXIST) {
if (errno == EEXIST) {
perror("the fifo file is exist:");
break;
}
perror("mkfifo failed:");
return -1;
}
}while(0);
rst = open(FIFO_PATH, O_RDWR);
if (rst == -1) {
perror("open failed:");
return -1;
} else {
wfd = rst;
}
i = 0;
while (1) {
memset(buffer, 0, 1024);
sprintf(buffer, "%s------%d", "abcdefg", i++);
write(wfd, buffer, strlen(buffer));
printf("---------%d-------\n", i);
sleep(5);
}
return 0;
}
Question: FIFO是不是和管道一样,单向半双工??(能不能打开之后随便读写)
经过测试得:管道可以以O_RDWR格式打开,如果时序处理得当,可以完成双向半双工通讯,其实可以理解为:可以以读写方式打开(这个只是人为的控制我们是否可以通过打开得到的文件描述符读操作或者写操作,并不代表人家不支持,只不过不可以同时读写而已),两端愿意写就写,愿意读就读,反正数据就在哪里,哪怕某个进程开心,自己写了然后在读出来都行。只是必须明白,管道先进先出后进后出,不管有多少写的,都是依次写进去的,而且读了就不再在管道里了,不读就一直在管道里,直到爆满。
fifoR.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/.fifo"
int main()
{
int rst, rfd, i;
char buffer[1024];
rst = mkfifo(FIFO_PATH, 0666);
do {
if (rst == -1 && errno == EEXIST) {
if (errno == EEXIST) {
perror("the fifo file is exist:");
break;
}
perror("mkfifo failed:");
return 0;
}
}while(0);
rst = open(FIFO_PATH, O_RDWR);
if (rst == -1) {
perror("open failed:");
return 0;
} else {
rfd = rst;
}
i = 0;
while (1) {
memset(buffer, 0, 1024);
read(rfd, buffer, 1024);
printf("---------%s-------\n", buffer);
memset(buffer, 0, 1024);
sprintf(buffer, "abcdefghijk---- %d", i++);
write(rfd, buffer, strlen(buffer));
sleep(6);
}
return 0;
}
fifoW.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_PATH "/tmp/.fifo"
int main()
{
int rst, i, wfd;
char buffer[1024];
rst = mkfifo(FIFO_PATH, 0666);
do {
if (rst == -1 && errno == EEXIST) {
if (errno == EEXIST) {
perror("the fifo file is exist:");
break;
}
perror("mkfifo failed:");
return 0;
}
}while(0);
rst = open(FIFO_PATH, O_RDWR);
if (rst == -1) {
perror("open failed:");
return 0;
} else {
wfd = rst;
}
i = 0;
while (1) {
memset(buffer, 0, 1024);
sprintf(buffer, "%s------%d", "1234567789", i++);
write(wfd, buffer, strlen(buffer));
printf("---------%d-------\n", i);
memset(buffer, 0, 1024);
sleep(3);
read(wfd, buffer, 1024);
printf("%s, --\n", buffer);
sleep(3);
}
return 0;
}
result:
NOTE:
以上的例子,只是为了让大家充分理解fifo的行为和本质, 它就是个指向内核的一段空间, 具有先进先出, 读了就没的特性的伪文件, 至于怎么合理的为我所用, 是develop的行为, 实际操作中我们并不会这么用。如果真的是双向的通信, 那还得加同步机制,麻烦的要死, 还不如选用其它的通信手段。
可以调用open打开FIFO, 打开规则如下:
1、当以阻塞(未指定O_NONBLOCK)方式只读打开FIFO的时候,则将会被阻塞(阻塞在open上哦),直到有其他进程以写方式打开该FIFO。
2、类似的,当以阻塞(未指定O_NONBLOCK)方式只写打开FIFO的时候,则将会被阻塞,直到有其他进程以读方式打开该FIFO。
3、如果FIFO的另一端已经被打开,那么O_NONBLOCK对open()调用不会产生任何影响。只有当FIFO的另一端还没有被打开的时候,O_NONBLOCK标记才会起作用,而具体产生的影响则依赖于打开FIFO是用于读取还是用于写入的:
探究发现,如果open时没有使用O_NONBLOCK(只有O_WRONLY 或RDONLY)参数,我们发现不论读端还是写端先打开,先打开者都会阻塞,一直阻塞到另一端打开。如果是粗暴的使用了O_RDWR, 则会直接成功返回
读者自己可以探究,如果open时使用了O_NONBLOCK参数,此时打开FIFO 又会是什么情况?
规则如下:
如果是为了读取(O_NONBLOCK |RDONLY),不管FIFO的写入端当前是否已经被打开,open()调用都会立即成功。
如果是为了写入O_NONBLOCK | O_WRONLY,并且还没有打开FIFO的另一端来读取数据,那么open()调用会失败,并将errno设置为 ENXIO(O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO open for reading.)。