六大进程通信机制总结( 二 )


有名管道匿名管道由于没有名字,只能用于父子进程间的通信 。为了克服这个缺点,提出了有名管道,也称做 FIFO,因为数据是先进先出的传输方式 。
所谓有名管道也就是提供一个路径名与之关联,这样,即使与创建有名管道的进程不存在亲缘关系的进程,只要可以访问该路径,就能够通过这个有名管道进行相互通信 。
使用 Linux 命令 mkfifo 来创建有名管道:
$ mkfifo myPipe【六大进程通信机制总结】myPipe 就是这个管道的名称,接下来,我们往 myPipe 这个有名管道中写入数据:
$ echo "hello" > myPipe执行这行命令后,你会发现它就停在这了,这是因为管道里的内容没有被读取,只有当管道里的数据被读完后,命令才可以正常退出 。于是,我们执行另外一个命令来读取这个有名管道里的数据:
$ cat < myPipehello3. 消息队列可以看出,管道这种进程通信方式虽然使用简单,但是效率比较低,不适合进程间频繁地交换数据,并且管道只能传输无格式的字节流 。为此,消息传递机制(Linux 中称消息队列)应用而生 。比如,A 进程要给 B 进程发送消息,A 进程把数据放在对应的消息队列后就可以正常返回了,B 进程在需要的时候自行去消息队列中读取数据就可以了 。同样的,B 进程要给 A 进程发送消息也是如此 。

六大进程通信机制总结

文章插图
 
消息队列的本质就是存放在内存中的消息的链表,而消息本质上是用户自定义的数据结构 。如果进程从消息队列中读取了某个消息,这个消息就会被从消息队列中删除 。对比一下管道机制:
  • 消息队列允许一个或多个进程向它写入或读取消息 。
  • 消息队列可以实现消息的随机查询,不一定非要以先进先出的次序读取消息,也可以按消息的类型读取 。比有名管道的先进先出原则更有优势 。
  • 对于消息队列来说,在某个进程往一个队列写入消息之前,并不需要另一个进程在该消息队列上等待消息的到达 。而对于管道来说,除非读进程已存在,否则先有写进程进行写入操作是没有意义的 。
  • 消息队列的生命周期随内核,如果没有释放消息队列或者没有关闭操作系统,消息队列就会一直存在 。而匿名管道随进程的创建而建立,随进程的结束而销毁 。
需要注意的是,消息队列对于交换较少数量的数据很有用,因为无需避免冲突 。但是,由于用户进程写入数据到内存中的消息队列时,会发生从用户态拷贝数据到内核态的过程;同样的,另一个用户进程读取内存中的消息数据时,会发生从内核态拷贝数据到用户态的过程 。因此,如果数据量较大,使用消息队列就会造成频繁的系统调用,也就是需要消耗更多的时间以便内核介入 。
4. 共享内存为了避免像消息队列那样频繁的拷贝消息、进行系统调用,共享内存机制出现了 。
顾名思义,共享内存就是允许不相干的进程将同一段物理内存连接到它们各自的地址空间中,使得这些进程可以访问同一个物理内存,这个物理内存就成为共享内存 。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程 。
集合内存管理的内容,我们来深入理解下共享内存的原理 。首先,每个进程都有属于自己的进程控制块(PCB)和逻辑地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的逻辑地址(虚拟地址)与物理地址进行映射,通过内存管理单元(MMU)进行管理 。两个不同进程的逻辑地址通过页表映射到物理空间的同一区域,它们所共同指向的这块区域就是共享内存 。
六大进程通信机制总结

文章插图
 
不同于消息队列频繁的系统调用,对于共享内存机制来说,仅在建立共享内存区域时需要系统调用,一旦建立共享内存,所有的访问都可作为常规内存访问,无需借助内核 。这样,数据就不需要在进程之间来回拷贝,所以这是最快的一种进程通信方式 。
六大进程通信机制总结

文章插图
 
5. 信号量和 PV 操作实际上,对具有多 CPU 系统的最新研究表明,在这类系统上,消息传递的性能其实是要优于共享内存的,因为消息队列无需避免冲突,而共享内存机制可能会发生冲突 。也就是说如果多个进程同时修改同一个共享内存,先来的那个进程写的内容就会被后来的覆盖 。


推荐阅读