FIFO
FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO,不相关的进程也能交换数据。
FIFO是Linux基础文件类型中的一种。但,FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。
有名管道突破了这种限制,可以实现互不相关的进程实现彼此的通信,管道可以通过路径名指定,在系统是可见的,建立管道之后就可以想普通文件一样进行读写,FIFO严格遵守先进先出的原则,读总是从开始读取数据,写数据写入末尾,不支持lseek()文件定位操作。
使用mkfifo函数创建FIFO
mkfifo函数就是用于创建一个FIFO文件
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
返回值说明:成功返回0; 失败返回-1,设置errno
参数pathname:要创建的FIFO文件名且该文件必须不存在
参数mode:指定FIFO文件的权限(8进制数,如0664)
一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可用于fifo。如:close、read、write、unlink等。
FIFO的打开规则
1)、如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
2)、如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。
总之,一旦设置了阻塞标志,调用mkfifo建立好之后,那么管道的两端读写必须分别打开,有任何一方未打开,则在调用open的时候就阻塞。对管道或者FIFO调用lseek,返回ESPIPE错误。
例子:
在当前目录创建FIFO文件
mkfifo("./test", 0664);
fifo_w.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int main() {
int fd = 0;
int len = 0;
int ret;
char buf[1024] = { 0 };
#if 1
//只写方式打开test
fd = open("./test", O_WRONLY);
if (fd < 0)
{
perror("open error");
exit(1);
}
#endif
#if 0
fd = open("./test", O_WRONLY | O_NONBLOCK);//设置成非阻塞
if (fd < 0)
{
//判断是否为对端没打开而导致ENXIO错误
if (errno == ENXIO)
{
perror("open error");
}
exit(1);
}
#endif
puts("open fifo write");
//向FIFO写入数据
while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0)
{
write(fd, buf, len);
}
close(fd);
return 0;
}
fifo_r.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int main() {
int fd = 0;
int len = 0;
char buf[1024] = { 0 };
//只读打开test
fd = open("./test", O_RDONLY);
if (fd < 0)
{
perror("open error");
exit(1);
}
puts("open fifo read");
//从管道中读取数据
while ((len = read(fd, buf, sizeof(buf))) > 0)
{
write(STDOUT_FILENO, buf, len);
}
//如果read返回0,说明读到文件末尾或对端已关闭
if (len == 0)
{
puts("peer is close or file end");
}
else
{
puts("read error");
}
close(fd);
return 0;
}
可以看到,先打开写端时,由于此时读端没有打开,所以写端会阻塞:
然后打开读端,就可以进行通信了:
最后,当写端按下Ctrl+d后关闭,对应的读端的read就会返回0。
使用非阻塞IO
如果在打开FIFO文件不希望阻塞时,在调用open函数可以指定O_NONBLOCK。
即在fifo_w.c中使用这段代码,然后我们先执行./fifo_w
,此时并未执行./fifo_r
。
#if 1
fd = open("test", O_WRONLY | O_NONBLOCK);//设置成非阻塞
if (fd < 0)
{
//判断是否为对端没打开而导致ENXIO错误
if (errno == ENXIO)
{
perror("open error");
}
exit(1);
}
#endif
出错原因在于,当你打开FIFO写入数据,但是对方没打开读端就会出现这样的错误。