操作系统(4)-进程间通信

进程间的通信方式,其实我们一直在用它,但是我们都不会去注意它。如果碰到面试官问你知道多少种进程间的通信方式,估计很多人都会有点懵。今天我们就来总结下进程间的通信方式有哪些。

一、概念

【百度百科】进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问的介质呢?进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。另外,系统空间是“公共场所”,各进程均可以访问,所以内核也可以提供这样的条件。此外,还有双方都可以访问的外设。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。

进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。IPC接口就提供了这种可能性。每个IPC方法均有它自己的优点和局限性,一般,对于单个程序而言使用所有的IPC方法是不常见的。

IPC方法包括管道(PIPE)、消息队列、共享内存、信号量及套接字(Socket)。

二、进程间通信的目的

  • 数据传输
    一个进程需要将其数据发送给另一进程,发送的数据量在一个字节到几M字节之间。
  • 共享数据
    多个进程操作共享数据
  • 事件通知
    一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 资源共享
    多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制
  • 进程控制
    有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

二、进程间通信方式介绍

1.管道

英文为pipe。这是一个我们在学习Linux命令行的时候就会引入的一个很重要的概念。它的发明人是道格拉斯.麦克罗伊,这位也是UNIX上早期shell的发明人。他在发明了shell之后,发现系统操作执行命令的时候,经常有需求要将一个程序的输出交给另一个程序进行处理,也因此,管道应运而生了。

管道可以分为两类:匿名管道和命名管道。匿名管道只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。命名管道:name_pipe克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

常见的Linux命令 "|" 其实就是匿名管道,表示把一个进程的输出传输到另外一个进程,如:

[root@xxx ~]# ps -ef | grep java

2.消息队列

注意,此消息队列不是我们常用的MQ,如kafka,rabbitmq,rocketmq等。

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。是由消息组成的链表,存放在内核中并由消息队列标识符标识。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

使用消息队列进行进程间通信,可能会收到数据块最大长度的限制约束等,这也是这种通信方式的缺点。如果频繁的发生进程间的通信行为,那么进程需要频繁地读取队列中的数据到内存,相当于间接地从一个进程拷贝到另一个进程,这需要花费时间。

3.共享内存

共享内存这个通信方式就可以很好着解决拷贝所消耗的时间了。系统加载一个进程的时候,分配给进程的内存并不是实际物理内存,而是虚拟内存空间。那么我们可以让两个进程各自拿出一块虚拟地址空间来,然后映射到相同的物理内存中,这样,两个进程虽然有着独立的虚拟内存空间,但有一部分却是映射到相同的物理内存,这就完成了内存共享机制了。

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信

4.信号量

共享内存最大的问题是什么?没错,就是多进程竞争内存的问题,就像类似于我们平时说的线程安全问题。如何解决这个问题?这个时候我们的信号量就上场了。

信号量的本质就是一个计数器,用来实现进程之间的互斥与同步。可以用来控制多个进程对共享资源的访问。例如信号量的初始值是 1,然后 a 进程来访问内存1的时候,我们就把信号量的值设为 0,然后进程b 也要来访问内存1的时候,看到信号量的值为 0 就知道已经有进程在访问内存1了,这个时候进程 b 就会访问不了内存1

信号量作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

5.套接字

套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。如我们的微信APP跟微信服务器通信,其实就是使用的Socket套接字进行通信的。

四、进程间通信方式比较

1、 管道:速度慢,容量有限,只有父子进程能通讯
2、 FIFO:任何进程间都能通讯,但速度慢;
3、 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题,消息队列可以不再局限于父子进程,而允许任意进程通过共享消息队列来实现进程间通信,并由系统调用函数来实现消息发送和接收之间的同步,从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用方便,但是信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合此种方法不太常用
4、 信号量:不能用来传递复杂消息,只能用来同步;
5、 共享内存:利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点共享内存块提供了在任意数量的进程之间进行高效双向通信的机制每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现

类型

无连接

可靠

流控制

记录

消息类型优先级

普通PIPE

N

Y

Y

 

N

流PIPE

N

Y

Y

 

N

命名PIPE(FIFO)

N

Y

Y

 

N

消息队列

N

Y

Y

 

Y

信号量

N

Y

Y

 

Y

共享存储

N

Y

Y

 

Y

UNIX流SOCKET

N

Y

Y

 

N

UNIX数据包SOCKET

Y

Y

N

 

N

五、进程间通信方式的选择

  • 常适用于两个进程间的通信
  • 共享内存用来实现进程间共享的、非常庞大的、读写操作频率很高的数据;这种方法适用于多进程间的通信
  • 其他考虑用socket。主要应用在分布式开发中

六、总结

这里总结下,进程间的通信方式有:

1、 管道;
2、 消息队列;
3、 共享内存;
4、 信号量;
5、 Socket;


参考资料:

1、 https://www.cnblogs.com/zgq0/p/8780893.html
2、 https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/1235923?fr=aladdin
3、 https://www.jianshu.com/p/8e4f9978e5b8

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: