进程间基于消息队列的通信_操作系统 - 消息传递系统中的进程间通信

在操作系统 - 共享内存系统中的进程间通信一文中我们讨论了在一个共享内存的环境中进行通讯。这要求了进程间共享一块内存区域,并且可以让应用编程人员通过代码访问和操纵这块共享内存。达到相同效果的另一种方法是,操作系统提供用于进程之间通过消息传递的工具来实现相互通信的。

消息传递提供了一种机制,这种机制允许进程在不分享相同地址空间的情况下,通讯和同步他们的行为。这在分布式系统中特别有用,因为通讯的进程为了链接在网络中的不同计算机上。比方说,一个因特网聊天程序被设计用于聊天的参与方与另一方交换信息。

一个消息传递机制至少提供两种操作:

  • send(message) 发送消息
  • receive(message) 接受消息

进程发送的消息的大小可以是固定的,也可以是可变的。如果只能发送固定大小的消息,则系统级实现是直接的。但是,这种限制使编程任务更加困难。相反,可变大小的消息需要更复杂的系统级实现,但编程任务变得更简单。这是在整个操作系统设计中常见的一种折衷。

如果进程P和Q想到通信,他们必须可以相互发送和接受消息:它们两个之间必须要有一条通讯链路。这个链路可以由好几种方式实现。这里我们关注的不是链路的物理实现而是逻辑上的实现。下下面是几种逻辑上的实现链路的方法。

  • 直接或间接通信
  • 同步或异步通信
  • 自动或显式缓冲

命名Naming

想要通讯的进程必须要有一种互相指待的方法。他们可以直接或间接通讯。

在直接通讯中,每个想要通讯的进程都必须显示地对通讯和发送或接受方命名。在这样的模式下,原本的send和receives会定义如下:

  • send(P, message) - 给进程P发送消息
  • receive(Q, message) - 接受来自进程Q的消息

这样模式下的通讯链路有如下属性:

  • 每对想要通讯的进程间的链路是自动创建的。进程需要互相知道对方的身份以便通信
  • 一条链路只与两个进程相关
  • 在两个进程之间只存在一条通讯链路

该方案在寻址方面表现出对称性。也就是说,发送方进程和接收方进程都必须命名对方才能进行通信。该方案的变体在寻址中采用不对称性。在这里,只有发件人为收件人命名;收件人无需为发件人命名。在这个方案中send()和receive()原语定义如下:

  • send(P, message) - 向进程P发送消息
  • receive(id, message) - 接收来自任何进程的消息。变量id被设置为发生通信的进程的名字。

这两种方式(对称和非对称)的缺点就是限制了进程定义的模块化程度。更改一个进程的名称可能必须要检查其它所有进程的定义。必须要发现所有对原名称的引用,以便于更换为新名称。总的来说,任何这样硬编码的技术,也是就是说标识符必须显性陈述的,都不是大家所想要的。

通过非直接通信,消息可以通过mailboxs或ports来发送和接收。一个mailbox可以被认为是一个抽象的目标,在这里消息可以被进程放入或移除。每个mailbox有一个独一无二的标识符。比方说,POSIX消息队列使用一个整数值来标识mailbox。一个进程可以通过不同的mailbox进行通讯,但两个进程只有共享一个mailbox是才可以通信。在这个方案中send()和receive()原语定义如下:

  • send(A, message) - 发送消息给mailbox A
  • receive(A, message) - 从mailbox A接收消息

在这个方案中,通信链路有如下属性:

  • 只有当进程双方有一个共享的mailbox时,进程间的链路才能被建立
  • 一个链路可能关联不止两个进程
  • 在每一对通讯进程之间,存在着不同的链路,每个链路对应着一个mailbox

现在,假定进程P1,P2,P3,共享mailbox A。进程P1发了一个消息给A,P2和P3执行receive()从A那里接收到消息。哪一个进程会接收到来自P1的消息。答案取决于如下挑选的方法:

  • 允许链路只能最多关联两个进程
  • 在同一时间,最多只有一个进程执行receive()操作。
  • 允许系统任意挑选一个进程来接受消息(也就是要么是P2,要么是P3,但不是都接收消息)。系统哦你会定义一个算法来选择哪个进程将接收消息(比如说,round robin,进程轮流接收消息)。系统可以向发送者识别接受者。

一个mailbox可以被一个进程或操作系统所拥有。如果mailbox是归进程所有的,那么我们把他们区分为owner(只能从mailbox接收消息)和user(只能向mail发送消息)。因为每个mailbox都有一个独一无二的owner,哪个进程应该接收发送至mailbox的消息就不会有困扰。但一个拥有mailbox的进程终止时,mailbox就没有了。任何之后发送给mailbox消息的进程必须被告知mailbox不再存在。

相反,一个被操作系统拥有的mailbox有他自己的地址空间。它是独立并且不附加于任何指定的进程。操作系统然后必须提供一个机制,这个机制允许进程的如下行为:

  • 创建一个新的mailbox
  • 通过这个mailbox发送和接收消息
  • 删除mailbox

创建mailbox的进程默认是mailbox的owner。一开始,owner只能从这个mailbox接收消息。然而,ownership和接收特权可以通过过适当的系统调用而传递出去。当然,这条规定可能会导致每个mailbox里都有大量的接收者。

同步

进程间的通信可以通过调用原语send()和receive()来发生。对于每个原语的实现有着不同的设计选择,消息传递可以是阻塞或非阻塞的,也被称为同步和异步。

  • 阻塞发送。发送信息的进程会一直被阻塞知道消息被接收进程或是mailbox接收
  • 非阻塞发送。发送信息的进程发送消息,然后返回继续其他的操作
  • 阻塞接收。接受者一直阻塞直到收到一条消息
  • 非阻塞接收。接受者要没收到一条消息,要没什么也没收到

send()和receives()存在着不同形式的组合。当send()和receive()都阻塞时,我们在发送者和接收者之间有一个集合点。当使用阻塞的send()和receive()语句时,生产者-消费者问题的解决方案就变得微不足道了。生产者仅调用阻塞的send()调用,并等待直到消息传递到接收者或邮箱。同样,当使用者调用receive()时,它将阻塞直到有消息可用为止。如下图所示:

5d6c1060c9182d66272b70fbadbbee59.png

缓冲Buffering

无论通讯是直接还是间接的,通讯的进程的消息交换是位于一个临时的队列中的。基本上,这样的队列可以有三种方式实现:

  • Zero capacity。队列的最长长度是零。因此链路不能在其中有任何等待的消息。在这样的情况下。发送者必须阻塞,直到接收者接收走消息。
  • Bounded capacity。队列是有有限长度的,因此,里面最多有n个消息。当新的消息发送后,队列没有满时,消息会被放入队列,且发送者可以不同等待而继续其他操作。链路的容量是有限的,因此,如果链路慢了,发送者就必须阻塞直到队列中有空间空出来了。
  • Unbounded capacity。队列的长度是无限的,因此会有许多消息在里面等待。发送者永远不会阻塞。

zero-capacity有时被指待没有缓冲的消息系统。其他几种情况指待带有自动缓冲的系统。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值