引入
pipe最大的劣势就是只能用于有共同祖先的各个进程之间,无法在没有亲缘关系的两个进程间创建一个管道并将它用作IPC通道。FIFO解决了这一点,它通过文件系统中的路径名作为名字来指定访问的进程。
命名管道FIFO、创建mkfifo
匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
FIFO文件在磁盘上没有数据块,仅用来标识内核中的一条通道。文件类型标识为p表示FIFO,文件大小为0。
我们可以通过命令行创建:
$ mkfifo filename
FIFO是一种文件类型,所以创建FIFO类似于创建一个文件。
我们也可以通过函数创建,就是mkfifo函数
NAME
mkfifo, mkfifoat - make a FIFO special file (a named pipe)
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
RETURN VALUE
On success mkfifo() and mkfifoat() return 0. In the case of an error, -1 is returned (in which case, errno is set appropriately).
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename,mode_t mode);
参数1为绝对路径字符串,参数2为指定文件权限位,即三位数字,或使用定义在#include <sys/stat.h>中的常值
返回:成功返回0,出错为-1
mkfifo函数已经隐含指定O_CREAT|O_EXCL(类似open函数)。也就是说,它要么创建一个新的FIFO,要么返回一个EEXIST错误。解决这一问题也很简单,只要在mkfifo之前用access函数判断文件是否已经存在就好了。
读端代码示例:
#include <iostream>
#include<unistd.h>//unix stand lib
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<dirent.h>//file dir
#include <sys/wait.h>//wait func
#include <stdlib.h>//ststem
#include <signal.h>
#include <string.h>
using namespace std;
#define FIFO1 "/tmp/teacher.fifo"
//=============================================读端====================
int main(int argc, char *argv[])
{
int fifo_fd;
char buffer[50];
bzero(buffer, sizeof(buffer));
int r_size = 0;
//access检查是否已经有这个文件,使得已经创建文件时不报错
if (access("/tmp/teacher.fifo", F_OK) == -1)///tmp/teacher.fifo文件不存在再创建
{
if (mkfifo("/tmp/teacher.fifo", 0664) == -1)
{
perror("mkfifo create:");
return -1;
}
}
//它是个文件,肯定要打开它,所以要定义文件描述符
fifo_fd = open("/tmp/teacher.fifo", O_RDONLY);//O_RDONLY只读
if (fifo_fd == -1)
{
perror("fifo_fd open error:");
return -1;
}
cout << "read " << endl;
r_size=read(fifo_fd, buffer, sizeof(buffer));//接收前strlen==0,所以必须用sizeof
cout << "receive buffer= " << buffer << endl;
while (1)
{
sleep(1);
}
return 0;
}
运行结果:
说明:
- FIFO文件也要用open和close函数来打开或关闭
- 传递给open调用的是一个FIFO文件的路径名,而不是一个正常文件的路径名
- 一个FIFO不能打开又读又写,也就是说open时模式参数不能为O_RDWR。
- 如果确实需要在程序之间双向传递数据的话,我们可以同时使用一对FIFO或管道,一个方向配一个;还可以用先关闭再重新打开FIFO的办法明确地改变数据流的方向
FIFO读写规则
为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO(上例)
O_NONBLOCK enable:立刻返回成功
为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
需要注意的是 命名管道与匿名管道的读写规则是一样的,参见上一篇
FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,这些工作完成之后,它们具有相同的语义。
The only difference between pipes and FIFOs is the manner in which
they are created and opened. Once these tasks have been accomplished,
I/O on pipes and FIFOs has exactly the same semantics.