进程间通信方式有多种,本文介绍的是管道,管道分为匿名管道和命名管道。
生活中的管道用来传输资源,例如水、石油之类的资源。而进程间通信的管道传输的是数据,管道的使用场景就是数据传输。
管道的特性:
半双工通信:数据可以沿两个方向发送,但是同一时间一个管道中只允许向一个方向发送数据,也可以称之为可以选择方向的单向通信。但实际使用管道的时候,方向一旦确定就不再更改了。就类似于家里的自来水管,自来水只会从水厂传输到家里,而不是从家里把水传输到水厂。
先进先出:先发送的数据先被接收,数据的发送顺序就是数据的接受顺序,就像队列一样,先进先出。(注意:管道中数据不能一直无限的写入,是有大小限制的)
如图:发送顺序是:1、2、3、4,接受顺序也是:1、2、3、4。
管道的本质:
内核中的一块缓冲区,而缓冲区就是内核空间中的一块内存,这块内存被用来进行进程间通信。
管道的分类:
理解匿名管道:
(1)内核中的缓冲区(也就是管道)没有标识符(注意和文件标识符区分开,这两不是一个东西),只能用于具有亲缘关系的进程间通信。
(2)因为标识符是一个管道的名字,一个管道如果没有名字,自然就是匿名管道。
匿名管道如何实现具有亲缘关系的进程间通信?
(1)在Linux中,系统在操作管道的时候,是把管道当作文件来进行操作的,因此管道都有对应的文件描述符。父进程先创建匿名管道,然后再创建子进程,这样父子进程就可以使用这个匿名管道来通信。
(2)因为父进程创建了匿名管道后,系统是把这个管道当作文件进行处理的,因此会有对应的文件描述符(文件描述符就是一个文件在fd_arr数组中的下标),文件描述符会占据数组中的最小未使用的下标。
(3)父进程创建子进程后,子进程会复制父进程pcb中的大部分信息,其中就包含了fd_arr[]数组,因此子进程也会得到文件描述符的相关信息。虽然子进程会初始化页表和虚拟空间,但不会修改pcb中和IO相关的信息。
(4)子进程复制fd_arr[]数组后,也就知道了这个管道的操作句柄,有了这个操作句柄,父子进程就可以访问同一个匿名管道了。
理解命名管道:
(1)内核中缓冲区具有标识符,也就是这个管道有名字。
(2)标识符是一个可见于文件系统的特殊管道文件,多个进程通过打开同一个标识符文件,就可以访问同一块内核中的缓冲区(命名管道)。
如图:管道文件标识符test.fifo(这是一个文件),进程通过这个文件来访问内核中的缓冲区(命名管道)。即便是没有亲缘关系的进程,也可以通过它来访问内核中的同一块缓冲区(命名管道)。
进程A要把数据通过管道发送给进程B,就首先要把数据拷贝一份放到内核空间的管道中(第一次拷贝)。然后进程B从内核空间的管道中把这个数据再拷贝一次带回来(第二次拷贝),这样才能拿到这个数据。
因此使用管道传输数据时,需要经历两次拷贝。
注意:如果父子进程都打开了这个管道,那么只有当父子进程对于这个管道的写端全都被关闭,读端在读完缓冲区后,才会返回0。
下一篇:代码规范(C语言)