进程间通信之——有名管道教程
1、概述
无名管道只能在具有亲缘关系的进程中使用,而有名管道可以在互不相关的两个进程间使用。有名管道将管道以文件的方式存储在指定路径中,使用ls -l可以看到第一个字符是‘p’,表示这是一个管道文件。文件操作用标准IO,即open,read,write,close。
2、函数介绍
2.1 创建管道文件
2.2.1 mkfifo
函数原型int mkfifo(const char *pathname, mode\_t mode);头文件sys/types.h、sys/stat.h功能创建一个管道文件参数
[in]:pathname:文件路径
[in]:mode:文件权限
返回成功返回0,失败返回-1
与有名信号量不同,管道文件是可以自己指定路径的。文件的权限也是与掩码相关的,(mode & (~umask))才是真正的文件权限,umask的值可以在终端直接输入umask查看。使用mkfifo创建的文件必须是不存在的,否则会出错,并置errno为EEXIST,此外还有其他错误码,可以通过在终端输入man 3 mkfifo查看。
3、测试程序
fifo1.c
1 /**
2 * filename: fifo1.c
3 * author: Suzkfly
4 * date: 2021-02-10
5 * platform: Ubuntu
6 * 配合fifo2使用,在一个终端执行fifo1,输入数据,按回车结束,另开1个终端执行
7 * fifo2,从fifo1中输入的数据能从fifo2中读出来。
8 */
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <string.h>
14
15 int main(int argc, const char *argv[])
16 {
17 int ret = 0;
18 int fd = 0;
19 char buf[128] = { 0 };
20
21 /* 创建fifo文件 */
22 #if 1
23 ret = mkfifo("myfifo", 0666);
24 if (ret == -1) {
25 printf("mkfifo failed\n");
26 return 0;
27 }
28 printf("ret = %d\n", ret);
29 #endif
30
31 /* 以只写方式打开fifo文件 */
32 fd = open("myfifo", O_WRONLY);
33 while (1) {
34 memset(buf, 0, sizeof(buf));
35 scanf("%s", buf);
36 ret = write(fd, buf, strlen(buf));
37 printf("write ret = %d\n", ret);
38 }
39
40 close(fd);
41
42 return 0;
43 }
fifo2.c
1 /**
2 * filename: fifo2.c
3 * author: Suzkfly
4 * date: 2021-02-10
5 * platform: Ubuntu
6 * 配合fifo1使用,在一个终端执行fifo1,输入数据,按回车结束,另开1个终端执行
7 * fifo2,从fifo1中输入的数据能从fifo2中读出来。
8 */
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <string.h>
14
15 int main(int argc, const char *argv[])
16 {
17 int ret = 0;
18 int fd = 0;
19 char buf[128] = { 0 };
20
21 /* 打开fifo文件,以只读方式打开 */
22 fd = open("myfifo", O_RDONLY);
23 while (1) {
24 memset(buf, 0, sizeof(buf));
25 ret = read(fd, buf, sizeof(buf));
26 printf("read ret = %d\n", ret);
27 printf("read data:%s\n", buf);
28 }
29
30 close(fd);
31
32 return 0;
33 }
测试结果:
代码分析:
测试例程代码比较简单,fifo1用来写数据,fifo2用来读数据,执行程序时需要先执行fifo1再执行fifo2,在fifo1的终端中输入数据,以回车键结束,在fifo2的终端中可以打印数据。
4、注意事项
- 如果一个进程以只写方式打开管道文件,如果没有其他进程以带有读的方式(只读/读写)打开管道文件,那么写数据的操作将会阻塞,直到管道文件以被带有读的方式打开。(写阻塞不是因为没有读数据,而是没有以带有读的方式打开管道文件)。但是如果有进程以带读的方式打开了管道文件,之后又将文件关闭了,此时写端再向管道写数据则会收到SIGPIPE(管道破裂)信号,默认情况下写数据的进程会直接退出。
- 如果一个进程以只读方式打开管道文件,如果没有其他进程以带有写的方式(只写/读写)打开管道文件,并且往管道内写数据,那么读操作将会阻塞。但是如果有进程以带写的方式打开了管道文件,之后又将文件关闭了,此时读端再向管道读数据则会收到SIGPIPE(管道破裂)信号,默认情况下写数据的进程会直接退出。
- 如果一个进程以读写方式打开管道文件,那么即使没有其他进程以带有读的方式打开管道文件,它往管道写数据的操作也不会阻塞,并且可以自己将数据读出来。
上面3条我总结一下,一个有名管道存在一个类似于建立连接的机制,有写无读时写阻塞,有读无写或未写时读阻塞,但一旦建立好连接,之后如果所有的写端关闭,那么读操作会导致管道破裂,如果所有的读端关闭,那么写操作会导致管道破裂。