前言(前情回顾)
进程君(父进程)在开发出匿名管道这门传音术后,解决了和自己孩子(子进程)间的沟通问题,父子关系趋于融洽。和孩子沟通后,进程君发现,自己脱离群众太久了,应该加强和群众的沟通。但是进程君与众位道友间没有血缘关系,无法使用匿名管道进行沟通。于是进程君决定改良匿名管道这种技术,让天下道友都能与自己畅通无阻的沟通,最终产生了一种无暇的传音技术——命名管道(FIFO)。
命名管道
我在上一篇文章中提到过:
“两个没有血缘关系的进程间可以同时打开相同的文件,进程内部分配对应的文件描述符,映射关系记录在PCB中,而两个进程间的fd分配时独立的,也就是fd在多进程中不是唯一的。当然我们也可以在进程1中向文件1写入数据,进程2从文件1中读取数据,构成一个伪管道。”
其实这里面的伪管道就是命名管道的意思。创建一个命名管道就是在Linux文件系统下创建一个特殊的fifo文件。与匿名管道的区别是,匿名管道也是文件,但它对文件系统不可见。而命名管道是一个可以在文件系统中看见的文件。
命名管道的创建:mkfifo()
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
这里的filename是一个字符串,描述了命名管道在文件系统中的路径,当字符串中没有‘/’出现是则管道创建在当前目录下。mode一般为0777,对应一个权限掩码,这里不做重点。直接写入即可。
我们先测试一下这个函数,在这里我创建了三个命名管道分别为demo1,demo2,demo3,编译后运行:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>int main() {int res1 = mkfifo("demo1",0777); // 创建于当前路径下int res2 = mkfifo("./demo2",0777); // 创建于当前路径下int res3 = mkfifo("../demo3",0777); // 创建于上一级路径下while(1);return 0;
}
观察运行前后的工程目录
可以发现运行后我们的工程目录下出现了三个新的文件,这三个文件就是我们的命名管道。这就是命名管道对文件系统是可见的这句话的含义。
相信一些脑洞大开的道友已经有了想法,既然命名管道在文件系统中可见,也就说明所有的进程都可以访问这个文件,那么只要我向这个进程中读写文件,是不是就完成了进程间的通信?
答案是:完全正确
如何实现对文件的读和写?不清楚的道友可以去看我之前写的一篇文章:linux多线(进)程编程——(1)前置知识
我们直接上代码,为了体现出命名管道与匿名管道的区别,这次我们要真正实现在两个程序间通信,所以我们要写两个C语言源文件。
proc1.c:创建命名管道并且向命名管道内写入数据
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {int res1 = mkfifo("fifo_demo", 0777);int wfd = open("fifo_demo", O_WRONLY); // 只写char send_buf[20];bzero(send_buf, 20);memcpy(send_buf, "hello, world!", 14);while(1) {write(wfd, send_buf, 20);sleep(1);}return 0;
}
proc2.c:接收管道内的数据
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {usleep(500*1000);// int res1 = mkfifo("fifo_demo", 0777);int rfd = open("fifo_demo", O_RDONLY); // 只读char recv_buf[20];while(1) {bzero(recv_buf, 20);read(rfd, recv_buf, 20);printf("%s\n", recv_buf);sleep(1);}return 0;
}
在命令行中运行程序,其中后面加一个&,表示在后台运行,让出终端
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ gcc -o proc1 proc1.c
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ gcc -o proc2 proc2.c
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ ./proc1&
[1] 9729
lockin@qingfenfuqin:~/work/linux_study/pipe/fifo$ ./proc2
hello, world!
hello, world!
hello, world!
hello, world!
...
注意:
(1)在两个程序中每次写入和读取的字节数量保持一致。
(2)在程序编写时就应该知道管道的名字。
(3)管道是单工通信,开发时不能又读又写,仅能以只读(O_RDONLY)或者只写(O_WRONLY)打开。
小结
这节课的知识点:
(1)命名管道的创建方法:mkfifo();
(2)命名管道与文件的关系,如何操作命名管道:read();write();
(3)如何在后台运行一个进程:&。
(4)命名管道与匿名管道的差异,以及对文件系统的可见性。
下一集我们将学习进程间第二中通信方式——共享内存的前置知识:linux多线(进)程编程——(5)虚拟内存与内存映射
结束语
“进程君开发出匿名管道与命名管道后,九天十地的道友终于可以畅通无阻的沟通交流了。”
听完这个修仙界传说,不知不觉间你的识海中也多了一道无暇神通,千里传音术——管道。
祝各位道友早日神功大成。