它是一种文件类型,在文件系统中可以看到。程序中可以查看文件stat结构中st_mode成员的值来判断文件是否是FIFO文件。创建一个FIFO文件类似于创建文件,FIFO文件就像普通文件一样。
文件概括
FIFO中可以很好地解决在无关进程间数据交换的要求,并且由于它们是存在于文件系统中的,这也提供了一种比匿名管道更持久稳定的通信办法。
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据。在shell中
mkfifo命令可以建立有名管道,下面通过一个实例来帮助读者理解FIFO。
相关指令
mkfifo命令的帮助手册如下所示:
mkfifo [option] name...
其中option选项中可以选择要创建FIFO的模式,使用形式为-m mode,这里mode指出将要创建FIFO的
八进制模式,注意,这里新创建的FIFO会像普通文件一样受到创建进程的
umask修正。在shell中输入命令如下:
–m 600 fifocat
<fifocat
$./recat > fifocat
$./recat > fifocat
#include
#include
#include
#include
#include
#define BUFES PIPE_BUF
int main ( void )
{
FILE *fp;
char * buf[BUFSZ];
...
...
...
pclose ( fp ) ; /*关闭管道*/
exit (0) ;
}
以上实例使用系统命令
mkfifo创建FIFO类型文件fifocat,并通过14.2.4节的程序recat来读取文件recat.c,将程序的标准输出从定向到fifocat中,再使用命令cat从fifocat读出数据。
文件创建
创建一个FIFO文件类似于创建文件,FIFO文件就像普通文件一样,也是可以经过路径名来访问的。相应文件stat结构的域st_mode的编码指明了文件是否是FIFO类型。FIFO管道通过函数
mkfifo创建,
函数原型如下:
#include
#include
int mkfifo( const char * filename, mode_t mode );
mkfifo函数中参数mode指定FIFO的读写权限,新创建FIFO的用户ID和组ID规则域open函数相同。参数filename指定新创建FIFO的文件名称。函数如果成功返回0,出 错返回–1,并更改
errno的值。errno有可能出现的值为:EACCESS、EEXIST、ENAMETOO- LONG、ENOENT、ENOSPE、ENOTDIR和EROFS。
下面实例演示了如何使用
mkfifo函数来创建一个FIFO。程序中从程序的
命令行参数中得到一个文件名,然后使用mkfifo函数创建FIFO文件。新创建的FIFO只具有读写权限。由于FIFO文件的特性,所以它被隐性地规定不具有执行权限。
程序清单14-5 create_fifo.c 使用mkfifo函数创建FIFO管道
#include
#include
#include
#include
#include
int main (int argc, char *argv[] )
{
mode_t mode = 0666; /*新创建的FIFO模式*/
if ( argc != 2 ){
/*向用户提示程序使用帮助*/
exit (1);
}
/* 使用
mkfifo函数创建一个FIFO管道*/
if ( ( mkfifo (argv[1], mode )) < 0) {
exit ( 1 );
}
else
/* 输出FIFO文件的名称 */
exit (0);
}
(2)在shell中编译该程序如下:
create_fifo.c–o create_fifo
(3)在shell中运行该程序如下:
$./ create_fifo
USEMSG: create_fifo {fifoname}
输入正确的命令符。
$./ create_fifo fifo1
you successfully create a FIFO name is :fifo1
$./ create_fifo fifo1
上述程序使用mkfifo函数创建一个FIFO,名字是基于用户的输入文件名,可以看到当要创建一个已经存在的FIFO时,程序会产生一个EEXIST的异常,相对应该异常,perror函数打印了相应的帮助信息为mkfifo: File exists。
读写操作
一般的I/O(open close read write unlink)函数都可以用于FIFO文件,需要注意的是,在使用open函数打开一个FIFO文件时,open函数参数flag标志位的O_NONBLOCK标志,它关系到函数的返回状态。详细说明如表14-2所示。
表14-2 open函数的flag(O_NONBLOCK)详细说明
FIFO的写操作规则类似于匿名管道的写操作规则,当没有进程为读打开FIFO,调用
write函数来进行写操作会产生信号SIGPIPE,则信号可以被捕捉或者完全忽略。
%注意:当FIFO的所有写进程都已经关闭,则为FIFO的读进程产生一个文件结束符。
FIFO的出现,极好地解决了系统在应用过程中产生的大量的中间临时文件的问题。FIFO可以被shell调用使数据从一个进程到另一个进程,系统不必为该中间通道去烦恼清理不必要的垃圾,或者去释放该通道的资源,它可以被留做后来的进程使用。并且规避了匿名管道在
作用域的限制,可应用于不相关的进程之间。
下面实例演示了使用FIFO来进行两个
进程间通信的例子。在程序write_fifo.c中打开一个名为fifo1的FIFO文件,并分10次向这个FIFO中写入数据。在程序read_fifo.c中先打开fifo1文件,读取里面的数据并输出到标准输出中。
程序清单14-6 write_fifo.c 使用FIFO进行通信
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFES PIPE_BUF
{
int fd ;
int n, i ;
char buf[BUFES];
time_t tp;
printfgetpid()); /*说明进程的ID*/
exit(1);
}
for ( i=0 ; i<10; i++){ /*循环10次向FIFO中写入数据*/
time(&tp); /*取系统当前时间*/
/*使用sprintf 函数向buf中格式化写入进程ID 和时间值*/
if((write(fd, buf, n+1))<0) { /*写入到FIFO中*/
close(fd); /* 关闭FIFO文件 */
exit(1);
}
sleep(3); /*进程睡眠3秒*/
}
close(fd); /* 关闭FIFO文件 */
exit(0);
}
程序中使用open函数打开一个名为fifo1的FIFO管道,并分10次向fifo1中写入字符串,其中的数据有当前进程ID以及写入时的系统时间。并把这个数据串输出到标准输出,然后程序自动睡眠3秒。
程序清单14-7 read_fifo.c 使用FIFO进行通信
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFES PIPE_BUF
{
int fd;
int len;
char buf[BUFES];
mode_t mode = 0666; /* FIFO文件的权限 */
{
exit(1);
}
while((len=read(fd,buf, BUFES))>0) /* 开始进行通信 */
close(fd); /* 关闭FIFO文件 */
exit(0);
}
程序中使用open函数以读方式打开一个名为fifo1的FIFO管道,并循环读出管道的数据,这里使用
while循环的作用就是确保数据可以全部读出,因为在读FIFO管道数据时,默认的是一次性读取PIPE_BUF个字节,当管道中数据多于PIPE_BUF个字节时,一次性读出PIPE_BUF-1个字节,然后read函数返回,再打印数据到标准输出。
(2)在shell中分别编译上述两个程序如下:
write_fifo.c–o write_fifo
read_fifo.c–o read_fifo
(3)在shell中使用
mkfifo创建程序中将要用到的FIFO管道。
–m 666 fifo1
(4)打开两个shell分别运行程序write_fifo 和程序 read_fifo。一个shell中输入如下:
$./write_fifo
i am 3708
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:01 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:04 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:07 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:10 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:13 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:16 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:19 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:22 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:25 2008
Send msg:write_fifo 3708 sends Thu Apr 17 18:26:28 2008
另一个shell中输入如下:
$./read_fifo
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:01 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:04 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:07 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:10 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:13 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:16 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:19 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:22 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:25 2008
read_fifo read: write_fifo 3708 sends Thu Apr 17 18:26:28 2008
上述例子可以扩展成客户端与服务器通信的实例,write_fifo的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,read_fifo类似于服务器,它适时监控着FIFO的读出端,当有数据时,读出并进行处理,但是有一个关键的问题是,每一个客户端必须预先知道服务器提供的FIFO接口,如图1所示。
图1 FIFO在客户端与服务器通信的应用1
文件缺点
当然FIFO也有它的局限性,如图2所示。
客户端可以发请求到服务器,但前提是要知道一个公共的FIFO通道,对于实现服务器回传应答到客户端的问题,可以通过为每一个客户端创建一个专用的FIFO,来实现回传应答。但也有不足,服务器会同时应答成千上万个客户端,创建如此多的FIFO是否会使系统负载过大,相应的如何判断客户端是否因意外而崩溃成为难题,或者客户端不读取应答直接退出,所以服务器必须处理SIGPIPE信号,并做相应处理。
%说明:在服务器端打开公共FIFO的时候,如果以读(O_RDONLY)打开,则当所有的客户端都退出时,服务器端会读取到文件结束符(read返回值为0)。这个问题的解决办法是服务器以读写(O_RDWR)打开公共FIFO,或者对read的返回值为0时进行特殊处理。如图2所示。服务器与客户端如何实现互相通信。
图2 FIFO在客户端与服务器通信的应用2