FIFO,也称为命名管道,它是一种文件类型。
特点
FIFO可以在无关的进程之间交换数据,即不要求交换数据的双方是父子进程,与无名管道不同。 FIFO有路径名与之相关联,它会生成一种特殊设备文件存在于文件系统中。
原型
int mkfifo(const char *pathname, mode_t mode);
其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。
当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。
例子
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//write_fifo.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
int main(int argc,char *argv[]){
int fd;
int n,i;
char buf[1024];
time_t tp;
printf("I am %d process.\n",getpid()); // 说明进程id
if((fd = open("fifo1",O_WRONLY))<0) // 只写方式打开FIFO
{
perror("Open FIFO failed!\n");
exit(1);
}
for(i = 0;i < 10; ++i){
time(&tp);
n = sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
printf("Send message:%s",buf);
if(write(fd,buf,n+1)<0){
perror("write FIFO");
close(fd);
exit(1);
}
sleep(1);
}
close(fd);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//read_fifo.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
int main(int argc,char *argv[]){
int fd;
int len;
char buf[1024];
if((mkfifo("fifo1",0666)<0)&&(errno!=EEXIST)) //创建失败且错误存在
perror("mkfifo");
if((fd = open("fifo1",O_RDONLY))<0) // 只读方式打开FIFO
{
perror("Open FIFO");
exit(1);
}
while((len = read(fd,buf,1024)) > 0)
printf("Read message:%s",buf);
close(fd);
return 0;
}
在两个终端里用 gcc 编译上面两个文件,先后台运行./fifo_read &
,然后运行./fifo_write
可以看到输出结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
I am 209 process.
Send message:Process 209's time is Wed Jul 10 02:35:10 2019
Read message:Process 209's time is Wed Jul 10 02:35:10 2019
Send message:Process 209's time is Wed Jul 10 02:35:11 2019
Read message:Process 209's time is Wed Jul 10 02:35:11 2019
Send message:Process 209's time is Wed Jul 10 02:35:12 2019
Read message:Process 209's time is Wed Jul 10 02:35:12 2019
Send message:Process 209's time is Wed Jul 10 02:35:13 2019
Read message:Process 209's time is Wed Jul 10 02:35:13 2019
Send message:Process 209's time is Wed Jul 10 02:35:14 2019
Read message:Process 209's time is Wed Jul 10 02:35:14 2019
Send message:Process 209's time is Wed Jul 10 02:35:15 2019
Read message:Process 209's time is Wed Jul 10 02:35:15 2019
Send message:Process 209's time is Wed Jul 10 02:35:16 2019
Read message:Process 209's time is Wed Jul 10 02:35:16 2019
Send message:Process 209's time is Wed Jul 10 02:35:17 2019
Read message:Process 209's time is Wed Jul 10 02:35:17 2019
Send message:Process 209's time is Wed Jul 10 02:35:18 2019
Read message:Process 209's time is Wed Jul 10 02:35:18 2019
Send message:Process 209's time is Wed Jul 10 02:35:19 2019
Read message:Process 209's time is Wed Jul 10 02:35:19 2019
上述例子可以扩展成 客户进程—服务器进程 通信的实例,write_fifo的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,read_fifo类似于服务器,它适时监控着FIFO的读端,当有数据时,读出并进行处理,但是有一个关键的问题是,每一个客户端必须预先知道服务器提供的FIFO接口,下图显示了这种安排:
探究
我发现fifo的执行结果非常神奇,忍不住进行进一步的探究。
在这里我做了几个小实验。
- 在fifo_read.c的open函数中加O_NONBLOCK修饰,会发现fifo_read进程会很快执行结束,这就是阻塞的开关。
- 在fifo_read.c中open属性未加O_NONBLOCK修饰的前提下,保持fifo_read在后台运行,在fifo_write中加入死循环,也让其在后台运行,会发现两个进程均不会结束,接下来kill掉fifo_write进程会发现两个进程均会结束。
结论是:
- 进程结束前及时不关闭文件句柄,进程结束后也会被回收。
- fifo_read进程中open(只读)函数在fifo_write进程中open(只写)函数执行后取消阻塞。
- fifo_read进程中read函数在fifo_write进程中write函数执行后或者文件句柄关闭后取消阻塞。