FIFO_zhuan

 

14.3  FIFO管道

FIFO也称为有名管道,它是一种文件类型,在文件系统中可以看到。程序中可以查看文件stat结构中st_mode成员的值来判断文件是否是FIFO文件。创建一个FIFO文件类似于创建文件,FIFO文件就像普通文件一样。本小节将介绍FIFO管道。

14.3.1  FIFO的概念

在本章14.2.2小节详细说明了匿名管道的缺点以及限制条件,在FIFO中可以很好地解决在无关进程间数据交换的要求,并且由于它们是存在于文件系统中的,这也提供了一种比匿名管道更持久稳定的通信办法。

FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据。在shell中mkfifo命令可以建立有名管道,下面通过一个实例来帮助读者理解FIFO。mkfifo命令的帮助手册如下所示:

mkfifo [option] name...

其中option选项中可以选择要创建FIFO的模式,使用形式为-m mode,这里mode指出将要创建FIFO的八进制模式,注意,这里新创建的FIFO会像普通文件一样受到创建进程的umask修正。在shell中输入命令如下:

$mkfifo –m 600 fifocat

$cat < fifocat

$./recat >fifocat

$./recat >fifocat

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <limits.h>

#define BUFES PIPE_BUF

int main ( void )

{

    FILE*fp;

    char* cmd = "cat file1";   /*shell 命令*/

    char* buf[BUFSZ];

...

...

...

    pclose ( fp ) ;      /*关闭管道*/

    exit (0) ;

}

$_

以上实例使用系统命令mkfifo创建FIFO类型文件fifocat,并通过14.2.4节的程序recat来读取文件recat.c,将程序的标准输出从定向到fifocat中,再使用命令cat从fifocat读出数据。

14.3.2  创建FIFO

创建一个FIFO文件类似于创建文件,FIFO文件就像普通文件一样,也是可以经过路径名来访问的。相应文件stat结构的域st_mode的编码指明了文件是否是FIFO类型。FIFO管道通过函数mkfifo创建,函数原型如下:

#include <sys/stat.h>

#include <sys/types.h>

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文件的特性,所以它被隐性地规定不具有执行权限。

(1)在vi编辑器中编辑该程序如下:

程序清单14-5  create_fifo.c 使用mkfifo函数创建FIFO管道

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

int main (int argc, char*argv[] )

{

    mode_t mode = 0666;    /*新创建的FIFO模式*/

   

    if ( argc != 2 ){

        /*向用户提示程序使用帮助*/

        printf("USEMSG: create_fifo {fifoname}/n");

        exit (1);

    }

   

    /* 使用mkfifo函数创建一个FIFO管道*/

    if ( ( mkfifo (argv[1], mode )) < 0) { 

        perror ( "failed to mkfifo" );

        exit ( 1 );

    }

    else

        printf ("you successfully create a FIFO name is : %s/n", argv[1]);
        /* 输出FIFO文件的名称 */

   

    exit (0);

}

(2)在shell中编译该程序如下:

$gcc 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: File exists

上述程序使用mkfifo函数创建一个FIFO,名字是基于用户的输入文件名,可以看到当要创建一个已经存在的FIFO时,程序会产生一个EEXIST的异常,相对应该异常,perror函数打印了相应的帮助信息为mkfifo: File exists。

14.3.3  FIFO的读写操作

一般的I/O(open close read write unlink)函数都可以用于FIFO文件,需要注意的是,在使用open函数打开一个FIFO文件时,open函数参数flag标志位的O_NONBLOCK标志,它关系到函数的返回状态。详细说明如表14-2所示。

表14-2  open函数的flag(O_NONBLOCK)详细说明

O_NONBLOCK标志

详 细 说 明

置位

只读open立即返回。当只写open时,如果没有进程为读打开FIFO,则返回–1,并置errno值为ENXIO

不置位

open视情况阻塞。只读open要阻塞到有进程为写打开FIFO,只写open要阻塞到有进程为读打开FIFO

FIFO的写操作规则类似于匿名管道的写操作规则,当没有进程为读打开FIFO,调用write函数来进行写操作会产生信号SIGPIPE,则信号可以被捕捉或者完全忽略。

%注意:当FIFO的所有写进程都已经关闭,则为FIFO的读进程产生一个文件结束符。

FIFO的出现,极好地解决了系统在应用过程中产生的大量的中间临时文件的问题。FIFO可以被shell调用使数据从一个进程到另一个进程,系统不必为该中间通道去烦恼清理不必要的垃圾,或者去释放该通道的资源,它可以被留做后来的进程使用。并且规避了匿名管道在作用域的限制,可应用于不相关的进程之间。

下面实例演示了使用FIFO来进行两个进程间通信的例子。在程序write_fifo.c中打开一个名为fifo1的FIFO文件,并分10次向这个FIFO中写入数据。在程序read_fifo.c中先打开fifo1文件,读取里面的数据并输出到标准输出中。

在vi编辑器中编辑该程序如下:

程序清单14-6  write_fifo.c 使用FIFO进行通信

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <stdio.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdlib.h>

#include <limits.h>

#define BUFES PIPE_BUF

int main(void)

{

    int fd ;

    int n, i ;

    char buf[BUFES];

    time_t tp;

   

    printf("I am %d/n",getpid());       /*说明进程的ID*/

    if((fd=open("fifo1",O_WRONLY))<0){  /*以写打开一个FIFO1*/

        perror("open");

        exit(1);

    }

    for ( i=0 ; i<10; i++){             /*循环10次向FIFO中写入数据*/

        time(&tp);                      /*取系统当前时间*/

        /*使用sprintf 函数向buf中格式化写入进程ID 和时间值*/

        n=sprintf(buf,"write_fifo %d sends %s",getpid(),ctime(&tp));

        printf("Send msg:%s/n",buf);

       

        if((write(fd, buf, n+1))<0) {   /*写入到FIFO中*/

            perror("write");

            close(fd);                  /* 关闭FIFO文件*/

            exit(1);

        }

        sleep(3);                       /*进程睡眠3秒*/

    }

   

    close(fd);                          /* 关闭FIFO文件*/

    exit(0);

}

程序中使用open函数打开一个名为fifo1的FIFO管道,并分10次向fifo1中写入字符串,其中的数据有当前进程ID以及写入时的系统时间。并把这个数据串输出到标准输出,然后程序自动睡眠3秒。

(1)在vi编辑器中编辑该程序如下:

程序清单14-7  read_fifo.c 使用FIFO进行通信

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <limits.h>

#include <fcntl.h>

#include <unistd.h>

#define BUFES PIPE_BUF

int main(void)

{

    int fd;

    int len;

    char buf[BUFES];

    mode_t mode = 0666;                 /* FIFO文件的权限*/

    if((fd=open("fifo1",O_RDONLY))<0)   /* 打开FIFO文件*/

{

        perror("open");

        exit(1);

    }

   

    while((len=read(fd,buf, BUFES))>0)  /* 开始进行通信*/

        printf("read_fifo read: %s",buf);

   

close(fd); /* 关闭FIFO文件*/

    exit(0);

}

程序中使用open函数以读方式打开一个名为fifo1的FIFO管道,并循环读出管道的数据,这里使用while循环的作用就是确保数据可以全部读出,因为在读FIFO管道数据时,默认的是一次性读取PIPE_BUF个字节,当管道中数据多于PIPE_BUF个字节时,一次性读出PIPE_BUF-1个字节,然后read函数返回,再打印数据到标准输出。

(2)在shell中分别编译上述两个程序如下:

$gcc write_fifo.c–o write_fifo

$gcc read_fifo.c–o read_fifo

(3)在shell中使用mkfifo创建程序中将要用到的FIFO管道。

$mkfifo –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接口,如图14-5所示。

图14-5  FIFO在客户端与服务器通信的应用1

14.3.4  FIFO的缺点

当然FIFO也有它的局限性,如图14-6所示。客户端可以发请求到服务器,但前提是要知道一个公共的FIFO通道,对于实现服务器回传应答到客户端的问题,可以通过为每一个客户端创建一个专用的FIFO,来实现回传应答。但也有不足,服务器会同时应答成千上万个客户端,创建如此多的FIFO是否会使系统负载过大,相应的如何判断客户端是否因意外而崩溃成为难题,或者客户端不读取应答直接退出,所以服务器必须处理SIGPIPE信号,并做相应处理。

%说明:在服务器端打开公共FIFO的时候,如果仅以读打开,则当所有的客户端都退出时,服务器端会读取到文件结束符。这个问题的解决办法是服务器以读写打开公共FIFO,如图14-6所示。服务器与客户端如何实现互相通信。

图14-6  FIFO在客户端与服务器通信的应用2

http://book.csdn.net/bookfiles/931/10093129298.shtml

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值