六大进程通信机制总结

初学操作系统的时候,我就一直懵逼,为啥进程同步与互斥机制里有信号量机制,进程通信里又有信号量机制,然后你再看网络上的各种面试题汇总或者博客,你会发现很多都是千篇一律的进程通信机制有哪些?进程同步与互斥机制鲜有人问津 。看多了我都想把 CSDN 屏了.....,最后知道真相的我只想说为啥不能一篇博客把东西写清楚,没头没尾真的浪费时间 。
希望这篇文章能够拯救某段时间和我一样被绕晕的小伙伴 。上篇文章我已经讲过进程间的同步与互斥机制,各位小伙伴看完这个再来看进程通信比较好 。
全文脉络思维导图如下:

六大进程通信机制总结

文章插图
 
1. 什么是进程通信顾名思义,进程通信( InterProcess Communication,IPC)就是指进程之间的信息交换 。实际上,进程的同步与互斥本质上也是一种进程通信(这也就是待会我们会在进程通信机制中看见信号量和 PV 操作的原因了),只不过它传输的仅仅是信号量,通过修改信号量,使得进程之间建立联系,相互协调和协同工作,但是它缺乏传递数据的能力 。
虽然存在某些情况,进程之间交换的信息量很少,比如仅仅交换某个状态信息,这样进程的同步与互斥机制完全可以胜任这项工作 。但是大多数情况下,进程之间需要交换大批数据,比如传送一批信息或整个文件,这就需要通过一种新的通信机制来完成,也就是所谓的进程通信 。
再来从操作系统层面直观的看一些进程通信:我们知道,为了保证安全,每个进程的用户地址空间都是独立的,一般而言一个进程不能直接访问另一个进程的地址空间,不过内核空间是每个进程都共享的,所以进程之间想要进行信息交换就必须通过内核 。
六大进程通信机制总结

文章插图
 
下面就来我们来列举一下 linux 内核提供的常见的进程通信机制:
  • 管道(也称作共享文件)
  • 消息队列(也称作消息传递)
  • 共享内存(也称作共享存储)
  • 信号量和 PV 操作
  • 信号
  • 套接字(Socket)
2. 管道匿名管道各位如果学过 Linux 命令,那对管道肯定不陌生,Linux 管道使用竖线 | 连接多个命令,这被称为管道符 。
$ command1 | command2以上这行代码就组成了一个管道,它的功能是将前一个命令(command1)的输出,作为后一个命令(command2)的输入,从这个功能描述中,我们可以看出管道中的数据只能单向流动,也就是半双工通信,如果想实现相互通信(全双工通信),我们需要创建两个管道才行 。
另外,通过管道符 | 创建的管道是匿名管道,用完了就会被自动销毁 。并且,匿名管道只能在具有亲缘关系(父子进程)的进程间使用,。也就是说,匿名管道只能用于父子进程之间的通信 。
在 Linux 的实际编码中,是通过 pipe 函数来创建匿名管道的,若创建成功则返回 0,创建失败就返回 -1:
int pipe (int fd[2]);该函数拥有一个存储空间为 2 的文件描述符数组:
  • fd[0] 指向管道的读端,fd[1] 指向管道的写端
  • fd[1] 的输出是 fd[0] 的输入
粗略的解释一下通过匿名管道实现进程间通信的步骤:
1)父进程创建两个匿名管道,管道 1(fd1[0]和 fd1[1])和管道 2(fd2[0] 和 fd2[1]);
因为管道的数据是单向流动的,所以要想实现数据双向通信,就需要两个管道,每个方向一个 。
2)父进程 fork 出子进程,于是对于这两个匿名管道,子进程也分别有两个文件描述符指向匿名管道的读写两端;
3)父进程关闭管道 1 的读端 fd1[0] 和 管道 2 的写端 fd2[1],子进程关闭管道 1 的写端 fd1[1] 和 管道 2 的读端 fd2[0],这样,管道 1 只能用于父进程写、子进程读;管道 2 只能用于父进程读、子进程写 。管道是用环形队列实现的,数据从写端流入从读端流出,这就实现了父子进程之间的双向通信 。
六大进程通信机制总结

文章插图
 
看完上面这些讲述,我们来理解下管道的本质是什么:对于管道两端的进程而言,管道就是一个文件(这也就是为啥管道也被称为共享文件机制的原因了),但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中 。
简单来说,管道的本质就是内核在内存中开辟了一个缓冲区,这个缓冲区与管道文件相关联,对管道文件的操作,被内核转换成对这块缓冲区的操作 。


推荐阅读