进程间通信之FIFO

FIFO有时被称为命名管道。管道只能由相关进程使用,这些相关进程的共同祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据

FIFO是一种文件类型(参考http://www.cnblogs.com/nufangrensheng/p/3501533.html)。stat结构(http://www.cnblogs.com/nufangrensheng/p/3501385.html)成员st_mode的编码指明文件是否是FIFO类型。可以用S_ISFIFO()宏对此进行测试。

创建FIFO类似于创建文件。确实,FIFO的路径名存在于文件系统中。

#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
返回值:若成功则返回0,若出错则返回-1

mkfifo函数中mode参数的规格说明与open函数中的mode相同(见http://www.cnblogs.com/nufangrensheng/p/3497789.html,具体的mode取值见http://www.cnblogs.com/nufangrensheng/p/3505129.html)。新FIFO的用户和组的所有权规则与http://www.cnblogs.com/nufangrensheng/p/3502133.html中所述的相同。

一旦已经用mkfifo创建了一个FIFO,就可用open打开它。其实,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。

应用程序可以用mknod函数创建FIFO。POSIX.1原先并没有包括mknod函数,它首先提出了mkfifo。mknod现在已包括在XSI扩展中。在大多数系统中,mkfifo调用mknod创建FIFO。

POSIX.1也包括了对mkfifo(1)命令的支持。于是,用一条shell命令就可以创建一个FIFO,然后用一般的shell I/O重定向对其进行访问。

当打开一个FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:

  • 在一般情况中(没有指定O_NONBLOCK),只读open要阻塞到某个其他进程为写而打开此FIFO。类似地,只写open要阻塞到某个其他进程为读而打开它。
  • 如果指定了O_NONBLOCK,则只读open立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写open将出错返回-1,其errno是ENXIO。

类似于管道,若用write写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。

一个给定的FIFO有多个写进程是很常见的。这就意味着如果不希望多个进程所写的数据互相穿插,则需考虑原子写操作。正如对于管道一样,常量PIPE_BUF说明了可被原子地写到FIFO的最大数据量。

FIFO有下面两种用途:

(1)FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。

(2)FIFO用于客户进程-服务器进程应用程序中,以在客户进程和服务器进程之间传送数据。

我们各用一个例子来说明这两种用途。

实例:用FIFO复制输出流

FIFO可被用于复制串行管道命令之间的输出流,于是也就不需要写数据到中间磁盘文件中(类似于使用管道以避免中间磁盘文件)。管道只能用于进程间的线性连接,然而,因为FIFO具有名字,所以它可用于非线性连接

考虑这样一个操作过程,它需要对一个经过过滤的输入流进行两次处理。图15-9表示了这种安排。

未命名

图15-9 对一个经过过滤的输入流进行两次处理

使用FIFO以及UNIX系统程序tee(1),就可以实现这样的过程而无需使用临时文件。(tee程序将其标准输入同时复制到其标准输出以及其命令行中包含的命名文件中。)

mkfifo fifo1
prog3 < fifo1 &
prog1 < infile | tee fifo1 | prog2

我们创建FIFO,然后在后台启动prog3,它从FIFO读数据。然后启动prog1,用tee将其输出发送到FIFO和prog2。图15-10显示了这种安排。

未命名

            图15-10 使用FIFO和tee将一个流发送到两个进程

实例:客户进程-服务器进程使用FIFO进行通信

FIFO的另一个应用是在客户进程和服务器进程之间传送数据。如果有一个服务器进程,它与很多客户进程有关,则每个客户进程都可将其请求写到一个该服务器进程创建的众所周知的FIFO中(“众所周知”的意思是:所有需要与服务器进程联系的客户进程都知道该FIFO的路径名)。图15-11显示了这种安排。因为对于该FIFO有多个写进程,客户进程发送给服务器进程的请求其长度要小于PIPE_BUF字节。这就能避免客户多个write之间的交错(http://www.cnblogs.com/nufangrensheng/p/3560130.html)。

未命名   

     图15-11 客户进程用FIFO向服务器进程发送请求  

在这种类型的客户进程-服务器进程通信中使用FIFO的问题是:服务器进程如何将回答送回各个客户进程。不能使用单个FIFO,因为服务器进程会发出对各个客户进程请求的响应,而请求者却不可能知道什么时候去读才能恰如其分地读到对它的响应。一种解决方法是每个客户进程都在其请求中包含它的进程ID。然后服务器进程为每个客户进程创建一个FIFO,所使用的路径名是以客户进程的进程ID为基础的。例如,服务器进程可以用名字/tmp/serv1.XXXXX创建FIFO,其中XXXXX被替换成客户进程的进程ID。图15-12显示了这种安排。

未命名

        图15-12 客户进程-服务器进程用FIFO进行通信

这种安排可以工作,但也有一些不足之处。其中之一是服务器进程不能判断一个客户进程是否崩溃终止,这就使得客户进程专用的FIFO会遗留在文件系统中。另一个不足之处是服务器进程必须捕捉SIGPIPE信号,因为客户进程在发送一个请求后没有读取响应就可能终止,于是留下一个只有写进程(服务器进程)而无读进程的客户进程专用FIFO。

按照图15-12中的安排,如果服务器进程以只读方式打开众所周知的FIFO(因为它只需读该FIFO),则每当客户进程数从1变成0时,服务器进程就将在FIFO中读到一个文件结束标记。为使服务器进程免于处理这种情况,一种常用的技巧是使服务器进程以读-写方式打开其FIFO。

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

posted @ 2014-02-23 09:09  ITtecman  阅读(3304)  评论(0编辑  收藏  举报