Linux

13.6 命名管道 ~ FIFO

2017-01-22  本文已影响89人  helinyu

命名管道 (有用的特点): 由于它们出现在文件系统中,所以他们可以像平常的文件名一样在命令中使用。
在创建的FIFO文件用在程序设计中之前,我们先通过普通的文件命令来观察FIFO 文件的行为。
实验:访问FIFO文件(首先空文件:/tmp/my_fifo)
(1)首先,尝试读取这个(空的)FIFO文件:如下(第一行是空的,第二行是在写入了字符之后重新读取的结果)


读取文件
向文件写入数据

(2)我们可以将读取的数据放在后台执行,这样可以一次执行两个命令:


mac实验内容
linux实验内容

实验解析:
因为FIFO 中没有数据,所以cat和echo程序被阻塞了,cat等待数据的到来,而echo 等待其他进程读取数据。 (这个是在后台,在前台可能会被中断)
在上面的第三步中,cat进程一开始就在后台被阻塞了,当echo向它提供了一些数据后,cat命令读取这些数据并把他们打印到标准输出上,然后cat程序退出,不再等待更多的数据。
它没有阻塞是因为当第二个命令将数据放入FIFO 后,管道将被关闭,所以cat程序中的read调用返回0字节,表示已经到达文件尾。

说明:与通过pipe 调用创建管道的不同,FIFO 是以命名文件的形式存在,而不是打开的文件描述符,所以在对它进行读写操作之前必须先打开它。FIFO也用open 和close 函数打开和关闭,这与我们前面看到的对文件的操作一样,但它多了一些其他的功能。对FIFO 来说,传递给open 调用的是FIFO的路径名,而不是一个正常的文件。

(1)使用open打开FIFO 文件

打开FIFO的一个主题限制是:程序不能以O_RDWR模式(是什么???)打开FIFO文件进行读写操作,这样做的后果并未明确定义。但这个限制是有道理的,因为我们通常使用FIFO只是为了单向传递数据,所以没有必要使用O_RDWR模式。如果一个管道以读/写方式打开,进程就会从这个管道读回它自己的输出。 (这个模式应该是读写双向操作)不过命名管道应该是为了单向设计的(管道应该都是)
若是确实需要在程序之间双向传递数据,最好使用一对FIFO或管道,一个方向使用一个,或者(但并不常用)采用先关闭再重新打开FIFO的方法来明确的改变数据流的方向。
(本章后面讨论用FIFO 进行双向数据交换的问题)
PS: 管道应该都是用于程序之间单向传递数据。

打开FIFO 文件和打开普通文件的另一点区别是,对open_flag (open函数的第二个参数)的O_NONBLOCK选项的用法。使用这个选项不仅改变open调用的处理方式,还会改变对这次open调用返回的文件描述符进行的读写请求的处理方式。

O_RDONLY / O_WRONLY / O_NONBLOCK 标志共有4中合法的组合方式,

open(const char *path , O_RDONLY); open调用将阻塞,除非有一个进程以写方式打开同一个FIFO,否则它不会返回。(与前面的cat类似)
open(const char *path, O_RDONLY | O_NONBLOCK);
即使没有其他进程以写方式打开FIFO,这个open调用也将成功并立刻返回。
open(const char *path, O_WRONLY);
open调用将阻塞,知道有一个进程以读方式打开同一个FIFO 为止。
open(const char *path, O_WRONLY | O_NONBLOCK);
这个函数调用总是立刻返回,但如果没有进程以读方式打开FIFO文件,open调用将返回一个错误的-1并且FIFO 也不会被打开。如果确实有一个进程以读的方式打开FIFO文件,那么我们就可以通过它返回的文件描述符对这个FIFO 文件进行写操作。

注意: O_ONOBLOCK 分别搭配O_RDONLY 和 O_WRONLY 在效果上的不同,如果没有进程以读方式打开管道,非阻塞写方式的open调用将失败,单非阻塞读方式的open调用总是成功。close调用的行为并不受O_ONOBLOCK标志的影响。

下面的例子是通过使用带O_NONBLOCK 标志的open调用的行为来同步两个进程。(这里没有选择使用多个实例程序的做法,而只是使用一个测试程序fifo2.c ,通过给该程序传递不同的参数来观察FIFO的行为)
源码在github上,


代码截图
运行步骤1
(2)运行结果

实验解析:

这个 程序能够在命令上指定我们希望使用的O_RDONLY、O_WRONLY 和O_NONBLOCK的组合方式。它会把命令行参数与程序中的常量字符串进行比较,如果匹配,就(用 |= 操作符)设置相应的标志。程序用access 函数俩检查FIFO 文件是否存在,如果不存在就创建它。
(在这个程序中,一直到最后都么有删除这个FIFO 文件,因为我们没有办法知道是否有其他的程序正在使用它。)

(2)不带O_NONBLOCK 标志的O_RDONLY 和O_WRONLY

测试程序可以使用不同的组合,注意:我们将第一陈旭(读取者)放在后台运行;


运行结果

这个可能是命名管道最常用的方法了,它允许启动读进程,并在open调用中等待,当第二程序打开FIFO文件时,两个程序继续运行。注意:读进程和写进程在open调用处取得同步。

注意:当一个Linux进程被阻塞时,它并不消耗CPU资源,所以这种进程的同步方式对CPU来说是非常有效的。

(3) 带O_NONBLOCK标志的O_RDONLY和不带盖标志的O_WRONLY

读进程执行open调用并立刻继续执行,及时没有写进程的存在。随后写程序开始执行,它也在执行open调用后立刻继续执行,但这次因为FIFO已被读进程打开。


看到并不会等待一定输入
这个过程和上面是有区别的

(4) 对FIFO进行读写操作

使用O_NONBLOCK模式会影响到FIFO的read和write调用。
对一个空的、阻塞的FIFO (即没有用O_NONBLOCK标志打开)的read调用将等待,知道有数据可以读时才执行。与此相反,对一个空的,非阻塞的FIFO 的read调用将立刻返回0字节。
对于一个完全阻塞的FIFO的write调用将等待,知道有数据可以被写入时才继续执行。如果FIFO不能接收所有写入的数据,它将按下面的规则执行。

使用FIFO实现进程间通讯。

(管道中的进程通讯是在亲缘的进程之间进行通讯)
命名管道可以在没有亲缘关系的命名管道进行通信的,我们需要用到两个独立的程序;
【分别是fifo3.c(生产者) 、fifo4.c(消费者)】

上一篇下一篇

猜你喜欢

热点阅读