文章目录
- 2.1_4 进程通信
- (一)什么是进程间通信
- (二)为什么进程通信需要操作系统支持
- (三)共享存储
- (四)消息传递
- (1)直接通信方式
- (2)间接通信方式
- (五)管道通信
- 总结
2.1_4 进程通信
(一)什么是进程间通信
进程间通信(Inter-Process Communication,IPC)是指两个进程之间产生数据交互。
例:在浏览“微博”的时候,可以把其中的帖子分享给“微信”好友。这就产生了进程间通信,发生了进程和进程之间的数据交互。
进程之间要想通信,需要操作系统的支持。
(二)为什么进程通信需要操作系统支持
首先,进程是分配系统资源的单位(包括内存地址空间),因此,各进程拥有的内存地址空间相互独立。
但是,若想发生进程间的数据交互,就意味着一个进程要去看看另一个进程的内存空间中存放的数据。但是,如果允许某进程直接访问另一个进程内存空间的数据,就意味着一个进程可以随意修改、读取其他进程的数据了。(例:你的手机中不知道什么时候安装了一个程序,这个程序直接把你微信的数据全部读取走了)这显然是很不安全的。
因此,出于安全的考虑,各个进程只能访问自己的内存地址空间,而不能访问其他进程的内存地址空间,无论是读、写,都不行。
因此,如果两个进程P、Q需要进行进程间通信,进程P不可能直接把数据写入进程Q的内存空间,所以肯定要依靠操作系统的支持才能完成进程间的通信。
接下来介绍三种实现“进程间通信”的机制。
(三)共享存储
一个进程可以申请一片“共享存储区”,而这个“共享存储区”也可以被其他进程所共享。
这样一来,进程P如果想给Q共享数据的话,它们就可以对“共享存储区”进行读、写操作,来实现通信。
注:通过“增加页表项/段表项”即可将同一片共享内存区映射到各个进程的地址空间中(第三章内容)。
问题:如果多个进程同时往“共享存储区”中写数据,可能会导致“写冲突”,可能有数据覆盖的问题,如何解决?
答:为避免出错,各个进程对共享空间的访问应该是互斥的。例如,当进程P访问共享空间时,其他进程就不能访问。
如何实现互斥访问?——各个进程可使用操作系统内核提供的同步互斥工具(如P、V操作,后面会讲)。
基于存储区的共享:操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式。
分配给你一片4KB的共享空间,在里面想在哪个位置读/写、想读/写多大的内容,都是自由的。
基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式。
例如这片共享空间是一个
int a[10]
,那么各个进程访问这个共享空间就要遵守数组
的读/写方式了。
(四)消息传递
进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。
(1)直接通信方式
每个进程的PCB当中,包含一个本进程的“消息队列”。消息队列中,存放着其他进程想要发送给此进程、应该被此进程接收的消息。
如图,进程P若想要给进程Q发送一个消息。
首先,进程P应当在自己的内存空间中创建并完善这个要发送的消息msg
。(包括消息头、消息体)
接下来,进程P使用操作系统提供的发送原语send(Q, msg)
。(该原语的参数指明了消息的接收者,要发送的消息)
最后,发送原语send(Q, msg)
会使得操作系统内核接收到这个消息,并且把这个消息挂在进程Q的消息队列里面。
进程Q若想要接收刚才P发来的这个消息msg
。
首先,进程Q使用操作系统提供的接收原语receive(P, &msg)
。(该原语的参数指明了消息的发送者,发送的消息)
接下来,操作系统内核会检查进程Q的消息队列,看看:这几个消息,到底哪个消息是由P发送过来的。
最后,找到此消息,并由操作系统内核将这条消息复制到进程Q的内存空间当中。
以上就是消息传递之——直接通信方式(点名道姓的消息传递)。
(2)间接通信方式
间接通信方式,以“信箱”作为中间实体进行消息传递。进程可以使用系统调用,向操作系统申请一个或多个“信箱”。
举例:
进程P向操作系统申请两个信箱,分别为信箱A
、信箱B
。
之后,进程P在自己的内存空间中,创建并完善一个消息msg
。
之后,进程P使用发送原语
来指明把哪个消息发送到哪个信箱。
注意:间接通信方式,在使用发送原语时,仅指明发送到哪个信箱,而并不指明发送给哪个进程。
之后,进程Q使用接收原语
来指明从哪个信箱接收哪个消息。
注意:可以多个进程往同一个信箱send
消息,也可以多个进程从同一个信箱中receive
消息。
(五)管道通信
和水管中的水流是一样的,数据的流向只能是单向的,而不能是双向同时进行的。
“管道”是一个特殊的共享文件,又名pipe
文件。其实就是在内存中开辟一个大小固定的内存缓冲区。
某个进程通过系统调用,来申请一个管道文件,操作系统会新建这个管道文件。这个文件的本质其实就是在内存当中开辟了一个大小固定的内存缓冲区,然后两个进程可以往这个内存缓冲区里面写数据或者读数据。但是数据的读写是先进先出的。
问题:
这样看来,“管道”其实就是开辟了一个内存缓冲区,那它和“共享存储”方式中的“共享存储区”不是没区别了吗?
答:
二者有区别。
对于“共享存储区”,进程P、Q在该区域内,想怎么读写就怎么读写,想从哪读写就从哪读写,没有任何的限制。
但是,对于“管道通信”方式,对于“管道文件”,既然进程P往里面写数据,同时进程Q从里面读数据,而且还要保证数据是先进先出的,就并不能想写到哪写到哪,想从哪读从哪读了。(实际上就是队列,出队只能从队头,入队只能从队尾,因此是有限制的。说的再准确点,就是循环队列)
注意:
1.管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。(向操作系统申请两个管道文件)
补充:(其实是计网中的概念)
全双工通信:两个方向的数据传输可以同时进行。
半双工通信:同时只允许单方向的传输。也可以等传输结束后切换为相反方向再进行传输,但同一时刻只能有一个方向的数据传输。
2.各进程要互斥地访问管道。
注:
对管道的互斥访问,是由操作系统本身就已经实现的,进程不需要关心。
3.当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。(毕竟管道文件是一个大小固定
的内存缓冲区)
4.当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
5.管道中的数据一旦被读出,就彻底消失。因此,当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:
a.一个管道允许多个写进程,一个读进程。(408真题官方答案)
b.允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据。(Linux的方案)