[2.1.4]进程管理——进程通信
创始人
2024-05-29 22:46:15
0

文章目录

  • 第二章 进程管理
    • 进程通信(IPC)
      • 为什么进程通信需要操作系统支持?
      • (一)共享存储
        • (1)基于存储区的共享
        • (2)基于数据结构的共享
      • (二)消息传递
        • 什么叫“格式化的消息”?
        • (1)直接通信方式
        • (2)间接通信方式
      • (三)管道通信
    • 小结

第二章 进程管理

进程通信(IPC)

  这个小节我们会学习进程通信的几种方式,分别是

  • 共享存储
  • 基于数据结构的共享
  • 基于存储区的共享
  • 消息传递
  • 直接通信方式
  • 间接通信方式
  • 管道通信

  进程之间的通信(Inter-Process Communication, IPC)是指两个进程之间产生数据交互。

  在一个系统当中,同时会有多个进程正在运行,那么这些进程之间难免需要相互配合着工作,在这种情况下,进程和进程之间的数据通信就显得很有必要了。

  比如正在浏览微博的时候,可以通过分享功能,把一条微博分享给微信好友。这时就发生进程间的通信了。本来那条微博的链接是在微博里面的,直接就分享到微信里面了,这个过程显然是进程和进程之间发生了数据交互、发生了通信 。

  既然进程之间的通信是很有必要的,那么该怎样实现进程之间的通信呢?这个需要操作系统的支持。

为什么进程通信需要操作系统支持?

  为什么进程之间的通信一定要有操作系统内核的支持,原因是这样的。

  我们系统中给各个进程分配内存地址空间的时候,各个进程的内存地址空间是相互独立的,比如进程P它可以访问自己的空间,进程Q可以访问自己的空间。但是进程P不可以访问进程Q的地址空间。

  这么规定,是出于安全的考虑。因为,如果一个进程可以随意地访问其他进程的内存地址空间,那么一个进程就可以随意修改其他进程的数据了,或者随意读取其他进程的数据。那这样的话,试想一下,比如你的手机里面不知什么时候安装了一个垃圾软件,这个垃圾软件,如果它可以随意地访问其他进程的地址空间,那有可能它直接把你微信里的私密的一些聊天数据,或者照片之类的,直接读取走了。这显然是不安全的。

image-20230307122348521

  因此,出于安全考虑,各个进程只能访问自己的这片内存地址空间,而不能访问其他进程的内存地址空间,无论是读、写,都不行。

  因此,如果P和Q,它们之间想要进行数据交互,想要进行进程之间的通信,那么显然,进程P是不可能直接把这个数据写到Q的这片空间里面的。

  所以,由于进程不可直接访问其他进程的内存地址空间,因此就必须要有操作系统的支持才可以完成进程之间的通信。


  接下来会介绍三种进程之间的通信方式:共享存储、消息传递、管道通信。

(一)共享存储

(1)基于存储区的共享

  各个进程只能访问自己的这片空间,但是如果操作系统支持共享存储的功能,那么一个进程,它可以申请一片共享存储区。而这片共享存储区,也可以被其他进程所共享。

image-20230307122907301

  这样的话,一个进程P,如果它要给Q传送数据的话,那么P就可以先把数据写到这片共享存储区里面,因为P对共享存储区是有访问权限的。接下来进程Q再从共享存储区里面读出数据。

  由于共享存储区可以被多个进程所共享,因此这些数据之间的数据交换,就可以通过这一片被共享的区域来进行。这就是共享存储的进程间通信方式。

  比如Linux中,如何实现共享内存:

  int shm_open(...); //通过shm_open系统调用,申请一片共享内存区

  void * mmap(...); //通过mmap系统调用,将共享内存区映射到进程自己的地址空间

  (注:什么叫内存区映射,这是第三章内容。通过“增加页表项/段表项”,即可将同一片共享内存区映射到各个进程的地址空间中。)

  另外,还需要注意一个问题,如果多个进程都往这片区域写数据的话,有可能会导致写冲突,会导致数据覆盖的问题。所以,各个进程之间如果使用共享存储的方式来进行通信的话,那么需要保证各个进程对这个共享存储区的访问互斥的。也就是当进程P正在访问这片区域的时候,那其他进程就不能访问这片区域。

  怎么实现这个互斥的功能呢?

  操作系统内核会提供一些同步互斥工具(比如在后面会学习的P、V操作),各个进程从而能够对共享存储区实现一种互斥的访问。

  刚才我们说的这种共享存储的方案,是基于存储区的共享。操作系统给你划定了这么大的一片区域,之后,若干个进程,到底是想往这片区域里的哪个位置写,或者从哪个位置读,这些都是很自由的。操作系统只负责把这片区域划给你,但是并不管你怎么使用这片区域。

image-20230307124232650

  基于存储区的共享:操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由参与通信的进程自己控制,而不是操作系统。这种共享方式速度更快,是一种高级通信方式。


(2)基于数据结构的共享

  相比之下,还有一种基于数据结构的共享。

  操作系统给你们两个进程,划定的共享区域,它就规定,只能存放一个长度为10的数组。这样的话,各个进程之间的通信自由度就没那么高,并且传送数据的速度也会比较慢。

image-20230307124751561

  基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式。

(二)消息传递

  如果采用这种通信方式,那么进程之间的数据交换会以格式化的消息(Message)为单位。通过操作系统提供的“发送消息/接收消息”两个原语来进行数据交换。


什么叫“格式化的消息”?

  所谓格式化的消息,由两个部分组成:消息头、消息体。

  消息头,要写明注明,这个消息是由谁发送的,到底要发送给谁,整个消息的长度是多少,等等这些概要性的信息。

  消息体,就是具体的,一个进程想要传送给另一个进程的数据。

image-20230307125216282


  这种消息传递的通信方式,又可以进一步划分为:直接通信方式、间接通信方式。

  其中,直接通信方式就是,发送进程要指明接收进程的ID。(系统里每一个进程都会有一个ID,叫PID)直接通信方式的意思就是,我发送的时候,直接点明,就是要它接收。

  而间接通信方式,会通过一个叫作“信箱”的中间实体来进行通信。所以间接通信方式又称为“信箱通信方式”。


(1)直接通信方式

  进程P现在要给进程Q发送一个消息,而在操作系统的内核区域是管理着各个进程的PCB的。

  同时,会有各个进程PCB对应的消息队列。比如进程Q就有一个进程Q的消息队列,也就是其他进程要发送给进程Q、应该被进程Q接收的这些消息,都挂在这个队列里面。

image-20230307125943691

  现在,进程P要给进程Q发送一个消息。首先,每个进程自己是有自己的地盘、自己的内存空间的,它会在此先完善这个消息的信息(如图msg),包括消息头、消息体。接下来,进程P会使用到发送原语send(Q, msg)(操作系统提供的发送原语),用它来指明,我的这个消息msg,是要发送给Q这个进程。

image-20230307130331995

  这个发送原语,会导致操作系统内核接收到这个消息,并且会把它 挂到进程Q的消息队列里面。

  此时,这个消息msg由进程P的内存空间,复制到了内核空间当中。

image-20230307130459897

  接下来,进程Q通过接收原语receive(P, &msg),来指明现在要接收一个消息,是P发来的。此时,操作系统会检查进程Q的消息队列,看一下这几个消息到底哪一个是由P发送过来的。

image-20230307130755318

  找到了由P发送过来的消息,那么操作系统内核会把这个消息体的数据,又从操作系统的内核区复制到进程Q的用户区、地址空间。

image-20230307130858159

  消息传递——直接通信方式:点名道姓的消息传递。我在发送的时候,指明要发送给谁;我在接收的时候,指明要接收谁发来的消息。

(2)间接通信方式

  刚才我们说过,间接通信方式,它需要一个中间实体(所谓的“信箱”)来进行消息的传递(所以又称之为信箱通信方式)。

  这种通信方式是这样来实现的:

  进程P和进程Q想要进行通信。那么进程P可以通过系统调用,申请一个信箱,当然也可以申请多个邮箱。比如此处,进程P申请了信箱A、信箱B。

image-20230307131207937

  现在,这两个进程怎么进行通信呢?

  首先,进程P在自己的地址空间里完善消息msg的内容,然后进程P可以使用发送原语send(A, msg),往信箱A发送消息msg

image-20230307131402790

  间接通信方式,是指明了我要发送到哪个信箱,并没有指明我要发送给哪个进程。

  那,进程Q在使用接收原语的时候,它可以指明,我要从信箱A中接收一个消息体。这样,信箱A中的这一个msg,就会被操作系统复制到进程Q的空间中了。

image-20230307131605378

  这就是使用信箱来完成消息传递的过程。

  通常来说,操作系统是可以允许多个进程往同一个信箱里send消息,也可以多个进程从同一个邮箱里receive消息。

(三)管道通信

image-20230307131742807

  “管道”这个词还是很形象的,它就像一个水管、管道一样。就是,写进程可以从管道的一边写入数据,读进程从管道的另一边取走数据。

  这个数据的流动只能是单向的。从左到右,或者从右到左,就像一根水管里的水流一样,不可以是双向同时进行的。

  这里的管道,其实是一种特殊的共享文件,又名pipe文件。也就是,如果两个进程要用管道的方式进行进程通信,那么首先我们需要系统调用的方式,来申请一个管道文件,操作系统会新建这个管道文件。这个文件的本质就是在内存当中开辟了一个大小固定的内存缓冲区。然后,两个进程可以往这个内存缓冲区里面写数据和读数据,但是这个数据的读写是先进先出的(FIFO)。


  问题:说到这里,管道通信是为两个进程开辟了一块内存缓冲区,而刚才所说的共享存储,也是开辟了一块共享存储区,也是可以被进程P、Q共享访问的。那么它们有什么区别?

  区别是这样的:

  刚才我们讲的基于存储区的共享,进程P、Q对于共享存储区中,具体的存储、读取位置,没有任何限制,很自由。但是,管道通信的方式,其中是一个数据流的形式,如果前边有空位,则写数据的时候要先往前边写,前边占满了再接着往后边区域写;读数据的时候也一样,只能先把前边的数据读空了,才可以读后边的这些数据,先进先出。

image-20230307132951609

  所以,管道通信和共享存储通信,区别还是很大的,管道通信的读写一定是先进先出的,可以把它理解为一个循环队列;而共享存储的读写是没有任何限制的。


  1、管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信(全双工通信),则需要设置两个管道

image-20230307133417392

  2、各进程要互斥地访问管道。(由操作系统实现)

  3、刚才已经介绍到,这个管道是一个大小固定的内存缓冲区,因此会被写满。当管道写满时,写进程阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。

  4、另一个方面,当管道读空时,读进程阻塞,直到写进程往管道中写入数据,即可唤醒读进程。

  5、(这一点,是很多教材最有争议的一个地方)管道通信的方式就决定了,一旦管道中的数据被读出,那就彻底消失了。所以,如果有多个读进程在读同一个管道的时候,就有可能导致错乱:因为我们管道里面的数据,它并没有指明我到底是要给进程Q的、还是要给进程R的。所以,如果多个进程都从同一个管道这读数据的话,那么就有可能这个数据的读取动作是比较乱的:第一块数据被Q读走了、第二块数据被R读走了。

  针对这一问题,不同的操作系统会有不同的解决方案:①一个管道允许多个写进程,一个读进程(2014年408真题高教社官方答案);②允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据(Linux的方案)。

  有的操作系统是①的方案,有的操作系统是②的方案。因此,有的教材按照①来说,管道允许多个写进程、但只允许一个读进程;而有的教材按照②来说,一个管道允许多个写进程、多个读进程。

  对于408考试来说,按照①来说即可。但是从现实应用的角度来看,①②都是存在的。

小结

image-20230307134655588

  共享存储、消息传递、管道通信这三种常见的进程间通信方式。这三种通信的功能都是需要操作系统的底层来支持的。


  共享存储,会设置一块共享内存区,并映射到进程的虚拟地址空间(这个问题在学完第三章,段表、页表之后再回来看就懂了,这个很简单)。

  另外,各个进程对共享空间的访问要互斥地进行,这个互斥的效果是由进程自己进行控制的,如使用P、V操作来实现。实际上,对于共享存储区的互斥访问,是一个经典的同步互斥问题:读者、写者问题(在后面会讲)。


  消息传递分为两种通信方式。直接通信方式就是要指名道姓地指明我要把消息发给哪个进程,然后操作系统会把这个消息直接挂到接收进程的消息队列里面。

  间接通信方式又叫信箱通信,操作系统会把消息放到指定的信箱当中,而消息的接收者也需要指明自己从哪个信箱当中取走消息。


  管道通信。对于操作系统而言,管道通信的这个管道,是一个特殊的共享文件(比如你在Linux系统上是的确能够找到这一文件的),但本质上这个文件就是一个内存缓冲区,如果结合数据结构的知识来看的话,这个内存缓冲区其实就是一个循环队列。那么,如果管道写满了,写进程就会被阻塞;如果管道读空了,读进程就会被阻塞。

  此外,一个管道文件只能实现半双工通信。这类似于现实生活中的一根水管,同一时刻,水不可能既从左往右流、又从右往左流。

  所以,如果想要从左往右、从右往左的水流同时存在的话,我可以建立两个管道,实现双向同时通信。


  读进程想要从管道读数据,只需保证一点:管道不是空的,即可。至于管道是否写满、还是只写了一部分,都无所谓,只要不是空的就能读。

  写进程同理,只要管道没满,还有空间让我可以写,就可以往管道中写数据。至于管道是完全空了、还是有一部分数据,都无所谓,只要不满就能写。

相关内容

热门资讯

122.(leaflet篇)l... 听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
Vue使用pdf-lib为文件... 之前也写过两篇预览pdf的,但是没有加水印,这是链接:Vu...
PyQt5数据库开发1 4.1... 文章目录 前言 步骤/方法 1 使用windows身份登录 2 启用混合登录模式 3 允许远程连接服...
Android studio ... 解决 Android studio 出现“The emulator process for AVD ...
Linux基础命令大全(上) ♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维...
再谈解决“因为文件包含病毒或潜... 前面出了一篇博文专门来解决“因为文件包含病毒或潜在的垃圾软件”的问题,其中第二种方法有...
南京邮电大学通达学院2023c... 题目展示 一.问题描述 实验题目1 定义一个学生类,其中包括如下内容: (1)私有数据成员 ①年龄 ...
PageObject 六大原则 PageObject六大原则: 1.封装服务的方法 2.不要暴露页面的细节 3.通过r...
【Linux网络编程】01:S... Socket多进程 OVERVIEWSocket多进程1.Server2.Client3.bug&...
数据结构刷题(二十五):122... 1.122. 买卖股票的最佳时机 II思路:贪心。把利润分解为每天为单位的维度,然后收...
浏览器事件循环 事件循环 浏览器的进程模型 何为进程? 程序运行需要有它自己专属的内存空间࿰...
8个免费图片/照片压缩工具帮您... 继续查看一些最好的图像压缩工具,以提升用户体验和存储空间以及网站使用支持。 无数图像压...
计算机二级Python备考(2... 目录  一、选择题 1.在Python语言中: 2.知识点 二、基本操作题 1. j...
端电压 相电压 线电压 记得刚接触矢量控制的时候,拿到板子,就赶紧去测各种波形,结...
如何使用Python检测和识别... 车牌检测与识别技术用途广泛,可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计...
带环链表详解 目录 一、什么是环形链表 二、判断是否为环形链表 2.1 具体题目 2.2 具体思路 2.3 思路的...
【C语言进阶:刨根究底字符串函... 本节重点内容: 深入理解strcpy函数的使用学会strcpy函数的模拟实现⚡strc...
Django web开发(一)... 文章目录前端开发1.快速开发网站2.标签2.1 编码2.2 title2.3 标题2.4 div和s...