[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系统上是的确能够找到这一文件的),但本质上这个文件就是一个内存缓冲区,如果结合数据结构的知识来看的话,这个内存缓冲区其实就是一个循环队列。那么,如果管道写满了,写进程就会被阻塞;如果管道读空了,读进程就会被阻塞。

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

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


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

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

相关内容

热门资讯

系统如何与安卓互通,技术融合与... 你有没有想过,你的手机系统竟然能和安卓系统这么默契地互通有无?这就像是一场跨越科技界的友谊赛,让我们...
安卓系统 扫码枪,安卓系统下扫... 你有没有想过,在繁忙的超市收银台,那些快速流畅的扫码操作,背后其实隐藏着一个小小的英雄——安卓系统扫...
平板插卡推荐安卓系统,安卓系统... 你有没有想过,你的平板电脑是不是也能像智能手机一样,随时随地扩充存储空间呢?没错,这就是今天我要跟你...
安卓系统固件安装失败,原因排查... 最近是不是你也遇到了安卓系统固件安装失败的问题?别急,让我来给你详细说说这个让人头疼的小麻烦,让你一...
ios系统和安卓区别,系统差异... 你有没有发现,现在手机市场上,iOS系统和安卓系统就像是一对双胞胎,长得差不多,但性格却截然不同。今...
安卓系统2.3优酷,优酷的崛起... 你有没有发现,安卓系统2.3时代的那股怀旧风?那时候,优酷可是视频界的巨头,多少人都是看着优酷长大的...
安卓导航系统密封,安卓导航系统... 你有没有发现,现在手机导航系统越来越智能了?尤其是安卓系统的导航,简直就像一个贴心的导航小助手,带你...
a版安卓11系统,a版深度解析... 你知道吗?最近手机界可是炸开了锅,各大品牌纷纷发布了搭载a版安卓11系统的手机。这可不是什么小打小闹...
安卓系统的模拟吉他,随时随地弹... 你有没有想过,在手机上也能弹奏吉他呢?没错,就是那种模拟吉他的安卓系统应用,让你随时随地都能享受音乐...
王者适配的安卓系统,深度解析适... 你有没有发现,最近玩《王者荣耀》的小伙伴们都在议论纷纷,说新出的安卓系统简直是为王者量身定做的!没错...
安卓系统自动定位关闭,隐私保护... 你有没有发现,手机里的安卓系统有时候会自动定位,这可真是让人又爱又恨啊!有时候,我们并不想让别人知道...
安卓系统电量耗尽测试,全面解析... 手机电量耗尽,这可是每个手机用户都头疼的问题。你有没有想过,你的安卓手机在电量耗尽前,到底经历了哪些...
如何升级车载安卓系统,车载安卓... 亲爱的车主朋友们,你是不是也和我一样,对车载安卓系统升级这件事充满了好奇和期待呢?想象当你驾驶着爱车...
安卓办公哪个系统好,深度解析哪... 你有没有想过,在安卓办公的世界里,哪个系统才是你的最佳拍档呢?在这个信息爆炸的时代,选择一个既强大又...
安卓系统差劲怎么解决,重拾流畅... 你有没有发现,安卓系统有时候真的让人头疼得要命?手机卡顿、应用崩溃、电池续航短,这些问题是不是让你抓...
喜欢安卓系统的原因,探索用户偏... 你有没有发现,身边的朋友、同事,甚至家人,越来越多的人开始使用安卓手机了呢?这可不是简单的潮流,而是...
安卓系统金立手机,品质生活新选... 你有没有发现,最近安卓系统下的金立手机突然火了起来?没错,就是那个曾经陪伴我们走过无数时光的金立手机...
无安卓系统的电视,新型无系统电... 亲爱的读者们,你是否厌倦了那些充斥着安卓系统的电视?想要尝试一些新鲜玩意儿?那就跟我一起探索一下无安...
麒麟系统能刷安卓系统吗,轻松刷... 你有没有想过,你的麒麟手机能不能装上安卓系统呢?这可是个让人好奇不已的问题。现在,就让我来带你一探究...
手机公司安卓系统吗,手机公司引... 你有没有想过,为什么你的手机里装的是安卓系统而不是苹果的iOS呢?这背后可是有着不少故事和门道的哦!...