【IPC通信】有名管道FIFO

时间:2022-05-18 19:33:30

前面学习过(匿名)管道(见前面博客),匿名管道只能用于有亲缘关系的各个进程之间,为了解决这个限制,UNIX系统进而引入了FIFO,也称为有名管道(named pipe)。

FIFO(first in, first out),是一个半双工数据流,也即一个半双工管道。不同于匿名管道的是,每个FIFO有一个路径名(或文件名)与之关联,也即FIFO的名字。有了名字,无亲缘关系的进程间就可以通过管道进行数据传输了。

创建FIFO的方式:

  • 使用shell命令 mkfifo创建一个有名管道
  • 使用C库函数mkfifo创建一个有名管道

使用shell命令 mkfifo创建一个有名管道

1 [infor@s123 FIFO]$ mkfifonpipe
2 [infor@s123 FIFO]$ ls-l
3 prw-r--r--  1 infor app 0 Nov 13 11:32 npipe

上面我们创建了一个有名管道npipe,我们可以看到有名管道其实是一个文件,文件类型是“p”,管道类型。

我们在开启两个终端,分别为A和B。在A终端下将数据写入管道,在B终端下将数据读出来。

1 [infor@s123 FIFO]$ ping10.4.123.124 >> npipe
2 #会阻塞在这里,等待另一个进程读

在终端A下将ping的结果写入管道npipe,这里会一直阻塞到另一个进程将数据全部读出或中止读出。

1 [infor@s123 FIFO]$ catnpipe
2 PING 10.4.123.124 (10.4.123.124) 56(84) bytes of data.
3 64 bytes from 10.4.123.124: icmp_seq=0 ttl=128time=0.838 ms
4 64 bytes from 10.4.123.124: icmp_seq=1 ttl=128time=0.835 ms
5 64 bytes from 10.4.123.124: icmp_seq=2 ttl=128time=0.843 ms
6 64 bytes from 10.4.123.124: icmp_seq=3 ttl=128time=0.834 ms

在终端B下读取npipe。

使用C库函数mkfifo创建一个有名管道

1 #include <sys/types.h>
2 #include <sys/stat.h>
3 /*
4 函数说明:依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限
5 返回值:若成功则返回0,否则返回-1,错误原因存于errno中
6 */
7 int mkfifo(const char *pathname, mode_t mode);

 对于创建方式,mkfifo隐含指定O_CREAT|O_EXCL,也即mkfifo创建一个新的FIFO,如果该FIFO已经存在,则会返回EEXIST错误。

对于参数mode可有如下选项:
S_IRUSR:当前用户可读
S_IWUSR:当前用户可写
S_IRGRP:组成员可读
S_IWGRP:组成员可写
S_IROTH:其他用户可读
S_IWOTH:其他用户可写

下面通过例子看下如何使用。

假如有这样一个案例。进程A执行完将执行结果写入管道,等待进程B将该结果读出。

01 #include <stdio.h>
02 #include <stdlib.h>
03 #include <sys/types.h>
04 #include <sys/stat.h>
05 #include <unistd.h>
06 #include <fcntl.h>
07 #include <errno.h>
08  
09 #define FIFO_NAME ("/tmp/fifo.1") // 设定FIFO的名字
10 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // 设定创建FIFO的权限
11  
12 /*创建一个FIFO,等待另一个进程读取*/
13 int main()
14 {
15     intwritefd;
16     charstatus[] ="success";
17     size_tn = 0;
18  
19     // 创建FIFO
20     if(mkfifo(FIFO_NAME, FILE_MODE) < 0)
21     {
22         if(EEXIST ==errno)
23         {
24             printf("FIFO:%s已经存在,不能重新创建\r\n", FIFO_NAME);
25         }
26         else
27         {
28             perror("创建FIFO错误");
29             exit(1);
30         }
31     }
32  
33     // 打开FIFO
34     writefd = open(FIFO_NAME, O_WRONLY, 0);
35     if(-1 == writefd)
36     {
37         perror("打开FIFO失败");
38         unlink(FIFO_NAME);
39         exit(1);
40     }
41  
42     // 写入执行状态
43     n = write(writefd, status,strlen(status));
44     if(n !=strlen(status))
45     {
46         perror("写入FIFO失败");
47         unlink(FIFO_NAME);
48         exit(1);
49     }
50  
51     if(-1 == close(writefd))
52     {
53         perror("关闭FIFO失败");
54         unlink(FIFO_NAME);
55         exit(1);
56     }
57  
58     return0;
59 }

编译并执行:
1 [infor@s123 FIFO]$ gcc -o wnpipe wnpipe.c
2 [infor@s123 FIFO]$ ./wnpipe
3 #阻塞在这里等待另外一个进程读取FIFO

这里我们看一下/tmp/fifo.1 是否生成:

1 [infor@s123 tmp]$ ls-l /tmp/fifo.1
2 prw-r--r--  1 infor app 0 Nov 13 12:58 /tmp/fifo.1

我们看到FIFO文件已经生成。

01 #include <stdio.h>
02 #include <stdlib.h>
03 #include <sys/types.h>
04 #include <sys/stat.h>
05 #include <unistd.h>
06 #include <fcntl.h>
07 #include <errno.h>
08  
09 #define FIFO_NAME ("/tmp/fifo.1") // 设定FIFO的名字
10 #define MAXLINE (1024)
11  
12 /*读取FIFO内容*/
13 int main()
14 {
15     intreadfd;
16     charstatus[MAXLINE];
17     size_tn = 0;
18  
19     // 打开FIFO
20     readfd = open(FIFO_NAME, O_RDONLY, 0);
21     if(-1 == readfd)
22     {
23         perror("打开FIFO失败");
24         unlink(FIFO_NAME);
25         exit(1);
26     }
27  
28     // 读取FIFO
29     n = read(readfd, status, MAXLINE);
30     if(-1 == n)
31     {
32         perror("读取FIFO失败");
33         exit(1);
34     }
35  
36     printf("管道内容:%s\r\n", status);
37  
38     // 关闭FIFO
39     if(-1 == close(readfd))
40     {
41         perror("关闭FIFO失败");
42         unlink(FIFO_NAME);
43         exit(1);
44     }
45  
46     // 删除FIFO
47     unlink(FIFO_NAME);
48  
49     return0;
50 }

编译并执行:

1 [infor@s123 FIFO]$ gcc -o rnpipe rnpipe.c
2 [infor@s123 FIFO]$ ./rnpipe
3 管道内容:success

这里我们再看一下/tmp/fifo.1 是否还存在:

1 [infor@s123 tmp]$ ls-l /tmp/fifo.1
2 ls: /tmp/fifo.1: No suchfileor directory