词条 | FIFO管道 |
释义 | FIFO也称为有名管道,它是一种文件类型,在文件系统中可以看到。程序中可以查看文件stat结构中st_mode成员的值来判断文件是否是FIFO文件。创建一个FIFO文件类似于创建文件,FIFO文件就像普通文件一样。 FIFO的概念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读出数据。 创建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}\"); 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\", 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。 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要阻塞到有进程为读打开FIFOFIFO的写操作规则类似于匿名管道的写操作规则,当没有进程为读打开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\",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\",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接口,如图1所示。 图1 FIFO在客户端与服务器通信的应用1 FIFO的缺点当然FIFO也有它的局限性,如图2所示。客户端可以发请求到服务器,但前提是要知道一个公共的FIFO通道,对于实现服务器回传应答到客户端的问题,可以通过为每一个客户端创建一个专用的FIFO,来实现回传应答。但也有不足,服务器会同时应答成千上万个客户端,创建如此多的FIFO是否会使系统负载过大,相应的如何判断客户端是否因意外而崩溃成为难题,或者客户端不读取应答直接退出,所以服务器必须处理SIGPIPE信号,并做相应处理。 %说明:在服务器端打开公共FIFO的时候,如果仅以读打开,则当所有的客户端都退出时,服务器端会读取到文件结束符。这个问题的解决办法是服务器以读写打开公共FIFO,如图2所示。服务器与客户端如何实现互相通信。 图2 FIFO在客户端与服务器通信的应用2 |
随便看 |
|
百科全书收录4421916条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。