跳到主要内容

远程进程之间的进程间通信

上一节所述的系统很容易推广, 从而允许地理位置不同的进程之间 —— 例如同一个计算机网络之内的进程之间 —— 进行进程间通信。

首先考虑一种简单的配置: 进程分布在星形的各个点上, 星形的每一个点上有一个自治的分时系统, 而在星形中心存在一个相当大、相当聪明的计算机系统, 称为网络控制器。这个中心系统里不能运行任何进程, 而应当把它看成网络中每个分时系统监控程序的一个延伸。

对读者来说应当很明显: 如果网络控制器能够执行 SEND、RECEIVE、SEND FROM ANY、RECEIVE ANY 和 UNIQUE 这些操作, 并且网络中所有分时系统的所有监控程序都不自行执行这些操作, 而是请网络控制器代它们执行, 那么我们就已经解决了远程进程之间进程间通信的问题。我们无需再做任何改动。

当我们假定网络控制器存在时, 一切之所以仍然照常工作, 是因为网络控制器能够记录哪些 RECEIVE 已被执行、哪些 SEND 已被执行, 并把它们配对, 正如模型分时系统中的监控程序所做的那样。全网范围的端口编号方案也是可能的, 只要网络控制器知道某个特定端口在某个特定时刻位于何处 (即在哪个站点)。

接下来考虑一个更复杂的网络: 其中没有共同的中心点, 因而必须把网络控制器所承担的功能分布到各个网络节点上。在本节余下的部分里, 我将说明: 可以把星形网络控制器所承担的功能高效而方便地分布到众多网络站点上, 同时仍然支持远程进程之间的一般进程间通信。

为了适应分布式网络, 必须对上面所述的四种 SEND/RECEIVE 操作各自做一些修改。为 RECEIVE 增加一个参数, 用以指明该 RECEIVE 所要发送到的站点。为 SEND FROM ANY 和 SEND 增加一个参数, 用以指明该 SEND 所要送达的站点, 尽管这通常就是本地站点。RECEIVE 和 RECEIVE ANY 都增加了获取所收到消息的来源站点的能力。于是, 当执行一次 RECEIVE 时, 该 RECEIVE 被发送到所指定的站点, 该站点可能是一个远程站点。与此同时, 一条 SEND 被发送到同一个站点, 通常就是执行该 SEND 的进程所在的本地站点。在这个称为会合点的站点上, RECEIVE 与适当的 SEND 配对, 消息传输于是被允许发往该 RECEIVE 所来自的那个站点。

RECEIVE ANY 从不离开它所在的站点, SEND FROM ANY 的必要性正在于此。必须能够把一条消息发送到一个 RECEIVE ANY 端口, 而又不让该消息在发送站点上因等待 RECEIVE 而被阻塞。当然, 也可以把系统构造成让 SEND/RECEIVE 在 RECEIVE 站点会合, 从而取消 SEND FROM ANY 操作, 但依我判断, 能够在源站点上阻塞一次普通 SEND 传输, 其价值远超过由此增加的复杂性。

各个站点的某处都保存着一张会合表。这张表为在该站点收到的每一个尚未配对的 SEND 或 RECEIVE 保存一个表项, 也为在该站点给出的所有 RECEIVE ANY 保存一个表项。一对匹配的 SEND/RECEIVE 一旦配对成功就立即从表中清除, 或许是在传输完成时清除。与模型分时系统中保存的那张类似的表一样, SEND 和 RECEIVE 表项如果长时间未能配对就会超时, 并通知发起者。RECEIVE ANY 表项则会在某个满足它的消息到来时从表中清除。

为了使网络控制器的功能得以分布, 最后一处必要的改动是给每个站点分配一部分唯一编号, 由它通过自己的 UNIQUE 操作来发放。这个话题我会在下文进一步讨论。

为了让读者清楚地了解分布式网络控制器如何工作, 下面给出一个例子。究竟由哪个进程挑选端口编号等等, 这些细节只是示例, 并不是本系统所规定的一项标准。

假设网络中有两个站点: K 和 L。站点 K 上的进程 A 希望与站点 L 上的进程 B 通信。进程 B 有一个 RECEIVE ANY 在端口 M 上挂起。

                    SITE K              SITE L
________ ________
/ \ / \
/ \ / \
/ \ / \
| Process A | | Process B |
| | | |
| | | |
\ / \ /
\ / \ port M /
\________/ \____^___/
|
RECEIVE ANY

幸好, 进程 A 知道站点 L 上存在端口 M, 于是使用 SEND FROM ANY 操作从端口 N 向端口 M 发送消息。消息中包含两个端口编号, 以及让进程 B 从端口 Q 向端口 P 给进程 A 发送消息的指示。站点 K 的站点编号连同该消息的 SEND 端口 N 一起被附加在这条消息上。

                   SITE K                        SITE L
________ ________
/ \ / \
/ \ / \
/ \ / \
| Process A | | Process B |
| | | |
| | | |
\ / \ /
\ port N /--->SEND FROM --->\ port M /
\________/ ANY \________/

to port M, site L
containing K, N, P, & Q

进程 A 现在执行一次在端口 P 上来自端口 Q 的 RECEIVE。进程 A 指定会合站点为站点 L。

                    SITE K                         SITE L
________ R ________
/ \ e / \
/ \ n T/ \
/ \ d a \
| | e b Process B |
| Process A | z l |
| | v e |
\ / o \ /
\ port P / RECEIVE ---> u \ /
\________/ MESSAGE s \________/

to site L
containing P, Q, & K

一条 RECEIVE 消息从站点 K 发送到站点 L, 并被登记在站点 L 的会合表中。在另一个时刻, 进程 B 执行一次从端口 Q 到端口 P 的 SEND, 并指定站点 L 为会合站点。

                    SITE K                         SITE L
________ R ________
/ \ e / \
/ \ n T/ \
/ \ d a \
| | e b Process B |
| Process A | z l |
| | v e |
\ / o \ /
\ port P / u <--- port Q /
\________/ SEND s \________/
to site L
containing P & Q

会合达成, 会合表项被清除, 到站点 K 上端口 P 的传输随即进行。这条传输的消息上附加了 SEND 站点的编号 (可能还有 SEND 端口的编号), 以供接收进程参考。

                SITE K                                SITE L
________ ________
/ \ / \
/ \ / \
/ \ / \
| Process A | | Process B |
| | | |
| | | |
\ port P / \ port Q /
\ / <---- transmission <---- \ /
\________/ to port T, site K \________/
containing data and L

进程 B 可能同时还想执行一次在端口 M 上来自端口 N 的 RECEIVE。

注意, 这个系统中只有一种重要的控制消息在站点之间移动, 也就是 [3] 中称为主机/主机协议消息的那种消息。这种控制消息就是 RECEIVE 消息。另外还有两种可能的站间控制消息: 当 RECEIVE 或 SEND 超时时发给发起站点的错误消息; 以及在会合站点不是 SEND 站点的罕见情形下发出的 SEND 消息。

当然, 端口之间的消息还必须有一种标准格式。例如下面这种:

    +-----------------+  +-----------------+  +-----------------+
| rendezvous site | | destination site| | source site |
+-----------------+ +-----------------+ +-----------------+
| RECEIVE port | | RECEIVE port | | RECEIVE port |
+-----------------+ +-----------------+ +-----------------+
| SEND port | | SEND port | | SEND port |
+-----------------+ +-----------------+ +-----------------+
| | | source port | | |
| | +-----------------+ | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| data | | data | | data |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
+-----------------+ +-----------------+ +-----------------+
transmitted transmitted received
by SEND by Network by RECEIVE
process Controller process

注意: 对于 SEND FROM ANY 消息, 会合站点就是目的站点。

在模型分时系统中, 可以把一个端口从一个进程传递给另一个进程。在分布式的网络控制器中这仍然是可能的。[对于端口传递的用处仍存疑虑的读者, 请阅读 [11] 中关于重新连接的章节。]

请记住, 要让一条消息从一个进程发送到另一个进程, 一次从端口 N 到端口 M 的 SEND 与一次在端口 M 上来自端口 N 的 RECEIVE 必须会合, 通常是在 SEND 站点会合。两个进程都记录它们各自认为的会合站点, 并把该站点作为相应操作的参数提供出来。执行 RECEIVE 的进程认为它就是 SEND 站点, 而执行 SEND 的进程通常也认为它就是 SEND 站点。由于一旦 SEND 与 RECEIVE 会合, 传输就被发往 RECEIVE 的来源, 而会合表中的表项被清除, 并且从 N 到 M 的每一次后续传输都必须重新建立该表项, 所以一个 RECEIVE 端口是很容易迁移的。如果一个进程把端口编号和会合站点编号都发送给位于某个其他站点的新进程, 而后者用这些相同的旧端口编号和会合站点说明执行一次 RECEIVE, 那么发送方永远不会知道接收方已经迁移。让一个 SEND 端口迁移则稍微困难一些。不过, 如果它确实迁移了, 那么一直用于某次 SEND 的那对端口编号以及原来的会合站点编号会被传递给新的站点。新 SEND 站点上的进程在从新站点发出第一次 SEND 时指明原来的会合站点。执行 RECEIVE 的进程也仍然认为会合站点是原来的站点, 因此 SEND 与 RECEIVE 会在原来的站点相遇。当它们相遇时, 该站点表中的表项被清除, SEND 消息的会合站点编号被改为发出该 SEND 消息的那个站点, 并且 SEND 与 RECEIVE 两条消息都被发送到新的 SEND 站点, 就像它们从一开始就注定要到那里一样。随后 SEND 与 RECEIVE 在新的会合站点再次相遇, 传输可以继续, 就好像这个端口从未迁移过一样。由于所有传输都含有来源站点编号, 后续的 RECEIVE 会被发送到新的会合站点。可以察觉到这种特殊处理必须发生, 是因为某条 SEND 消息在一个并非发出该 SEND 消息的站点上被收到。一切都如此容易改变, 是因为不存在需要断开和迁移的永久连接, 不像 ARPA 网络曾经提议的那种重新连接方案 [10][11]; 也就是说, 在本文所述系统中连接只是短暂存在, 因此可以在任何一对进程之间重新建立, 只要它们在某个时刻碰巧知道彼此的端口编号, 并且对各自所在的位置有所了解。

当然, 所有这一切本来也可以由进程通过来回发送消息、通告任何可能的迁移和新的站点编号来完成。