进程间的通信

进程间通信(IPC,InterProcess Communication)是指 不同进程之间传播或交换信息。 这通常只发生在一个系统中。

通信可以有两种类型 -

  • 具有亲缘关系的进程之间的通信,如父进程和子进程。
  • 在不相关的进程之间

在进一步讨论此主题之前需要了解的一些重要术语。

  • 管道 - 两个相关过程之间的通信。 该机制是半双工的,意味着只能从第一个进程与第二个进程通信。 为了实现全双工,即要对于两个之间来回通信,就需要两个管道了。
  • FIFO - 两个不相关进程之间的通信。 FIFO是全双工的,这意味着第一个进程可以与第二个进程进行通信,反之亦然。
  • 消息队列 - 两个或多个具有全双工容量的进程之间的通信。 这些进程将通过发布消息并将其从队列中取出来相互通信。 一旦检索后,消息就不再在队列中可用。
  • 共享内存 - 两个或多个进程之间的通信是通过所有进程之间共享的内存来实现的。 共享内存需要通过同步对所有进程的访问来保护彼此。
  • 信号灯 - 信号灯用于同步对多个进程的访问。 当一个进程想要访问内存(用于读取或写入)时,需要在访问被移除时被锁定(或保护)并释放。 这需要所有进程重复以保护数据。
  • 信号 - 信号是通过信号的方式在多个过程之间进行通信的机制。 这意味着源进程将发送一个信号(由数字识别),目标进程将相应地处理它。
     

 

一、管道

管道,通常指无名管道

管道是多个相关进程之间的通信媒介。 它可以在一个进程内,也可以在子进程和父进程之间进行通信。 通信是通过一个过程写入管道和从管道读取来实现的。 要实现管道系统调用,请创建两个文件,一个写入文件,另一个从文件读取。

管道机制可以用一个实时的场景来看,比如用管子把水灌进一个容器里。 填充过程可以理解为是写入管道,读取过程只不过是从管道中取出。 这意味着一个输出(水)是为另一个(桶)输入的。参考下图 -
                              

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。要关闭管道只需将这两个文件描述符关闭即可。

特点总结:

  1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
  2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

 

二、命名管道

FIFO,也称为命名管道,它是一种文件类型。

管道是用于相关过程之间的通信。 我们是否可以使用管道进行不相关的进程通信,比方说,我们要从一个终端执行客户端程序,从另一个终端执行服务器程序? 答案是否定的。那么怎样才能实现不相关的进程通信,简单的答案就是使用 命名管道。 即使这适用于相关的进程,但是使用命名管道进行相关的进程通信没有任何意义。

我们使用一个管道进行单向通信,两个管道进行双向通信。也可以使用单一命名管道作为命名管道支持双向通信(服务器和客户端之间的通信,同时还有客户端和服务器之间的通信)。

命名管道的另一个名称是FIFO(先进先出)。 系统调用(mknod())来创建一个命名管道,这是一种特殊的文件。

FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。

特点总结:

  1. FIFO可以在无关的进程之间交换数据,与无名管道不同。

  2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

 

三、共享内存

共享内存是多个进程共享一个给定的存储区。

                                                        

我们知道,为了在两个或多个进程之间进行通信,使用共享内存。但是在使用共享内存之前,系统调用需要完成,让我们看看 -

  • 创建共享内存段或使用已经创建的共享内存段(shmget())
  • 将进程附加到已经创建的共享内存段(shmat())
  • 从已连接的共享内存段(shmdt())中分离进程
  • 共享内存段上的控制操作(shmctl())

特点总结

  1. 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。

  2. 因为多个进程可以同时操作,所以需要进行同步。

  3. 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问

 

四、消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

为什么已经拥有了共享内存时需要消息队列呢? 这将是多种原因,让我们将其分解为多个点来简化 -

  • 据了解,一旦消息被一个进程接收到,它将不再可用于任何其他进程。 而在共享内存中,数据可供多个进程访问。
  • 如果想使用小信息格式进行通信。
  • 当多个进程同时进行通信时,共享内存数据需要同步保护。
  • 使用共享内存的写入和读取频率很高,那么实现功能将会非常复杂。 在这种情况下不值得使用。
  • 如果所有的进程不需要访问共享内存,但是很少的进程只需要它,那么用消息队列实现会更好。
  • 如果想要与不同的数据包进行通信,比如进程A正在发送消息类型1给进程B,消息类型10给进程C,消息类型20给进程D。在这种情况下,用消息队列实现是比较简单的。 
  • 当然,消息队列的顺序是FIFO(先进先出)。 插入到队列中的第一条消息是第一条要检索的消息。

使用共享内存或消息队列取决于应用程序的需要以及如何有效地使用它。

使用消息队列的通信可以通过以下方式进行 -

  • 通过一个进程写入共享内存,并由另一个进程读取共享内存。 正如我们所知道的,读取也可以用多个进程来完成。
                               
  • 通过一个进程用不同的数据包写入共享存储器,并通过多个进程,即按照消息类型读出。
                                 
    看到消息队列中的某些信息后,现在是检查支持消息队列的系统调用(System V)的时候了。

要使用消息队列执行通信,请执行以下步骤 -

  1. 创建一个消息队列或连接到一个已经存在的消息队列(msgget())
  2. 写入消息队列(msgsnd())
  3. 从消息队列中读取(msgrcv())
  4. 对消息队列(msgctl())执行控制操作

 

特点总结

  1. 消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
  2. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
  3. 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
  4. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
     

 

五、信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据

首先想到的问题是,为什么我们需要信号量? 一个简单的答案,以保护多个进程共享的关键/共同区域。

假设多个进程正在使用相同的代码区域,如果所有人都想并行访问,那么结果是重叠的。 例如,多个用户仅使用一台打印机(通用/关键部分),例如3个用户,同时给予3个作业,如果所有作业并行启动,则一个用户输出与另一个用户输出重叠。 因此,我们需要使用信号量来保护这个信号,即当一个进程正在运行时锁定关键部分,并在完成时解锁。 这将为每个用户/进程重复,以便一个作业不与另一个作业重叠。

基本上信号量分为两类 -

  • 二进制信号 - 只有两个状态01,即锁定/解锁或可用/不可用,互斥实现。
  • 计算信号量 - 允许任意资源计数的信号量称为计数信号量。

要使用信号量执行同步,请执行以下步骤 -

  1. 创建一个信号量或连接到一个已经存在的信号量(semget())
  2. 对信号量执行操作,即分配或释放或等待资源(semop())
  3. 在消息队列(semctl())上执行控制操作

特点总结:

  1. 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。

  2. 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

  3. 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

  4. 支持信号量组。

 

 

总结:

1、为什么会有进程间通信?

  • 每个进程都有自己的地址空间,如果任何进程想要将自己的地址空间的某些信息与其他进程进行通信,那么只能通过IPC(进程间通信)技术进行。

2、进程通信的方式?

  • 通信可以在相关或不相关的进程之间进行。
  1. 使用管道命名管道来执行相互关联的进程通信。
  2. 使用命名管道或通过共享内存消息队列的常用IPC技术执行无关的进程(例如在一个终端中运行一个进程而在另一个终端中运行另一个进程)通信。

3、五种通讯方式总结

  1. 管道:速度慢,容量有限,只有在相互关联的进程间通信    
  2. FIFO:任何进程间都能通讯,但速度慢    
  3. 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题    
  4. 信号量:不能传递复杂消息,只能用来同步    
  5. 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
     

                                                       

 

 

参考:进程间通信(IPC)介绍  进程间通信教程​​​​​​​ 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值