什么是管道
管道是最基本的进程间通讯,它是半双工的通讯(数据流是定向的)。它创建于内存缓冲区中,用于连接一个写进程一个读进程,大小刚好为一个页(4KB)。一个进程把数据写入管道,由内核定向的流入另一个读进程。(多进程通讯中要用锁控制,防止一个进程在读取管道里数据没读完就被其他进程读走之类的问题)
管道概念
- 管道用的是利用文件系统file结构,和VFS索引节点inode来完成的结构。通过两个file文件指向一个l临时VFS节点,VFS再指向一个物理页面。
- 当管道数据被一个读进程读走后,数据就被删除了,其他读进程无法继续读取被读走的数据
- 写进程是在管道尾部写入,读进程是在管道头部读取。
- 当管道空了时候,read()函数会阻塞,当管道满了时候,write()函数会阻塞,也可以通过0_ONBLOCK调成非阻塞
- 如果管道对应的读端关闭了文件描述符,则write产生一个信号SIGPIPE
- 如果写端关闭,则read返回0
管道的分类
(1)有名管道
(2)无名管道
无名管道
无名管道故名思义,无名即是无文件标识,在系统文件中不可见。那么问题来了读进程和写进程如何指向同一块内存缓冲区空间呢?这里就是需要用父子进程这种关系来创建一个无名管道。
创建无名管道
先用图形来介绍下创建的流程
![](https://i-blog.csdnimg.cn/blog_migrate/709999016906f37e1ea2b7265159b08c.png)
第一步是创建无名管道
int fd[2];
pipe(int fd[2];
介绍下pipe这个函数
int pipe(int fd[2])
头文件是:#include<unistd.h>
传入的参数是int fd[2](是一个保存两个int的一维数组)。当实参传入是fd[2]时候2变的无意义,它退化成一个指针。
fd[0]是指向管道的读端,fd[1]是指向管道写端的。这个是不可反转的。
![](https://i-blog.csdnimg.cn/blog_migrate/92a8abdb0ec4ccb4b0c479071544b158.png)
第二步就是fork()一下进程A,产生子进程B,如右边图片所示。
这个时候进程A fork()产生了一个子进程B,因为fork时候文件描述符表也会被复制到子进程中。
那么就会出现有两个读端和两个写端的指向管道。就解决了我们一开始提出的问题无名管道无文件标识,如何指向同一块的内存缓存器空间呢。
接下来就是要确定哪一个进程作为读端和写端,这里我们以进程A为写端为例,我们这个时候需要修改下进程A对管道操作的权限,就是在fork立即关闭读端close(fd[0])。我们要把进程B变成读端,就要先close(fd[1])。如果在程序结束最后才关闭的话,虽然程序执行可能会成功,但是进程对管道的权限是不对的,这里安全性不够。
到这一步基本也算完成了无名管道的创建了。
剩下的都是读写的操作
我们以一个小题目为例子
我们将一个进程A把标准输入的数据,通过管道定向传给进程B。进程B把读取到的数据大写变成小写,传回去给进程B。
1)创建好两个管道,fork一个子进程。
![](https://i-blog.csdnimg.cn/blog_migrate/cc0056ab979886716965dd095a1d3e6b.png)
2)子进程把读到的数据,处理好,通过管道2传回给进程A
![](https://i-blog.csdnimg.cn/blog_migrate/c135af1d9dfdb2854164e438e8eda881.png)
3)父进程就是就是标准输入buff,写入管道1中,通过管道2把处理好的数据读出来。
![](https://i-blog.csdnimg.cn/blog_migrate/284c197ee5d3f2124d49f1c798d8f73d.png)
代码算是完成了。
有名管道
有名管道是为了解决无名管道所面对的问题,无名管道的限制是必须是有亲缘关系的进程。
有名管道的限制是进程必须有访问管道的权限。
有名管道就是先创一个管道文件,这个管道文件会在目录树一个文件标识,然后通过open()函数进行操作,然后进程间通讯。如果只有一个进程打开管道,则该进程会阻塞在open处,直到另一方进程也open了,那么就open成功,且才会开辟内存缓冲区的空间。当双方通讯完成,关闭了文件描述符,之后,临时VFS索引节点丢弃,然后系统释放内存缓冲区那么部分被开辟的空间。
有名管道创建
1.直接在shell下建立,通过mkfifo命令来创建
mkfifo FIFO
也可以通过在函数里创建
int mkfifo(const char *pathname,mode_t mode);
当代码运行后,如果创建管道是在函数里完成的,在运行该程序的第一次创建完成,就会一直存在目录中,除非删除该文件,如果该管道文件存在那么创建就会失败。
一个进程创建了管道,另个进程就没必要继续管道了,也就是管道只要有一个进程创建就够了,另个进程只要打开管道。
创建好的管道名会存在磁盘里,但是实际的系统的调用还是由mknod()来完成的。
这样就创建好了管道文件
2然后通过open函数来打开和设置操作管道文件权限
3.在和无名管道操作一样进行读写操作,完成数据的传输
我们演示和无名管道一样的使用说明
![](https://i-blog.csdnimg.cn/blog_migrate/fb44bd505ab6b052bdd9b1a44834f3b0.png)