IV - 用户控制与通信协议
必须存在这样一个进程: 它同意监听网络中的任何人, 并在对方给出适当身份证明后为它创建一个进程。该进程称为 logger, 它通过网络相关的用户控制与通信 (UCC) 模块经由 NCP 进行交互, 而该模块实现了所需的协议。除了唯一一种情形 (关闭已死进程的连接) 之外, 运行 UCC 模块的进程没有特殊的网络特权。
在 UCC 协议下, 一个已指示创建 "外部" 进程的 "请求方" 进程维护两条全双工伪打字机连接: 一条连接到外部 logger, 另一条连接到被创建的进程。通往外部 logger 的双工连接用于向 logger 表明请求方进程的身份, 并在登录之后向请求方进程返回有关被创建进程健康状况的基本信息。通往被创建进程的双工连接则用于向它进行控制通信。
维护两条全双工连接可以避免重连问题: 无论当 logger 把通信移交给被创建的进程时, 还是当它需要重新取得控制时, 都是如此。代价则不大: 只要求请求方进程在两组连接之间切换打字机通信。
建立通信的方式大体如下: 请求方进程首先保留四个 socket 码连续的 socket。然后它向 UCC "发信号", 并指明其中某个 socket。UCC 从该 "信号" 得知是哪个进程在呼叫, 并且依据协议得知 UCC 应在哪一对请求方 socket 上与请求方进程通信, 以及被创建的进程应使用哪一对请求方 socket 进行通信。下文将更详细地规定这一点。
建立并操作一个外部进程
每台主机上的 UCC 始终把一个用户号 = 0、实例标记 = 0 的发送 socket 作为 "signal" socket 保持打开 (活动但未连接), 并定期检查是否有发往该 socket 的 INIT。希望在本主机上创建进程的进程必须首先通过向该 socket 发出一条 INIT 来向 UCC 发信号。
请求方进程必须拥有四个 socket 码连续的空闲 socket: 从 <base_socket> (接收) 到 <base_socket+3> (发送)。编号较高的一组发送/接收 socket 用于与外部 UCC 的打字机通信, 编号较低的一组用于与被创建进程的打字机通信。
-
"请求方" 进程调用两次 LISTEN, 以打开 <base_socket+2> 和 <base_socket+3> 这一接收/发送对, 它将通过这一对与外部 UCC 交谈。然后它在 <base_socket> 上向 UCC 的 "signal" socket 发出一条 "发信号用的" INIT 调用。UCC 对这条 "发信号用的" INIT 调用所做的唯一事情, 就是记下它所源自的 socket 编号 <base_socket>。UCC 立即拒绝该请求, 以便让它的 "signal" socket 对其他信号保持打开。
-
在它对 UCC 信号 socket 的首次 INIT 调用收到预期的 REJECT 之后, 请求方进程对 <base_socket> 和 <base_socket+1> 发出 LISTEN。(被创建的进程将 INIT 这些 socket, 以建立与请求方进程之间的控制通信。) 随后请求方进程通过调用 STATUS <base_socket+2> 进入阻塞。
-
UCC 把一对空闲的发送/接收 socket INIT 到请求方的 <base_socket+2> 和 <base_socket+3>, 请求方进程想必正在这一对上 LISTEN。请求方进程在对这两个 socket 执行 LISTEN 之后, 已带着阻塞选项调用了 STATUS <base_socket+2>, 因此当来自外部 UCC 的 INIT 到达请求方进程时, STATUS 会带着 INIT 指示返回。请求方进程核实发出呼叫的正是该 UCC, 然后 ACCEPT 该呼叫。接着请求方进程调用 STATUS <base_socket+3>, 并在该 socket 的 INIT 到达时返回。它做一次类似的核实与 ACCEPT。(请求方进程先对哪一个 socket 调用 STATUS 是任意选择的。) 当请求方进程接受了来自 UCC 的两条 INIT 之后, 双向通信即建立。这条连接在登录仪式期间以及被创建进程的整个生命期内都得到维持。如果请求方进程未能在有限的时间内对 UCC 的 INIT 作出适当响应, UCC 就放弃这次建立连接的尝试。
-
然后请求方进程必须与 UCC 进行登录仪式。(初始协议可能会把登录仪式标准化。) 如果 logger 不满意并希望切断请求方, UCC 模块会 CLOSE 掉 <base_socket+2> 和 <base_socket+3>, 或许在 logger 发出一条适当消息之后这样做。
-
如果满意, logger 就为该用户创建一个进程。UCC 与请求方保持直接通信, 但这条连接现在只用于报告有关被创建进程的基本信息。
-
被创建进程的第一项任务是与它的请求方进程建立一条双路伪打字机控制连接。被创建的进程把它的一对发送/接收 socket INIT 到请求方的 <base_socket> 和 <base_socket+1>。如果两个请求都被 ACCEPT, 被创建的进程就通过这条连接发送一条初始消息。然后它进入命令级, 在该级别上等待通过该连接到来的打字机命令消息。如果被创建的进程无法与请求方进程建立双工通信, 它就应当销毁自身。UCC 要么 CLOSE 掉自己与请求方之间的连接, 要么安排创建另一个进程。
-
当一个被创建的进程被注销时, UCC 使用对 NCP 的一条特权入口来 CLOSE 该已死进程与其他进程之间的所有连接, 并使该已死进程的所有打开的 socket 变为非活动。UCC 向请求方进程回传一条消息, 然后 CLOSE 它与请求方进程之间的那两条连接。
-
当 INTERRUPT 调用从请求方进程通过请求方的接收 socket <base_socket> 发往一个被创建的进程时, 它具有标准的 "退出" 含义。来自被创建进程的所有待处理输出都被中止, 它随后进入 "命令级", 在那里等待通过通往请求方进程的打字机连接到来的命令。被中断的处理可以通过向被创建的进程发出 "start" 命令来恢复。(请注意, 关于待处理输出的这条规则比 INT NCP 命令所实现的规则更为严格。)
本文件是借助 MULTICS 的 "runoff" 命令生成的。先用 "qed" 文本编辑器创建了一个由文本和 "runoff" 请求混杂而成的源文件。然后用 "runoff" 命令编译该文件, 以产生一份定稿。本文件的最新版本以如下段名在线存放于 MULTICS 中
>udd>Multics>Meyer>network_protocol.runoff(END)
REQUESTOR FOREIGN
PROCESS LOGGER
-------------- -------------
a. LISTEN to sockets
<base_socket+2> and
<base_socket+3> to be
connected to foreign logger.
b. INIT <base_socket>
to "signal" socket of
foreign logger.
=======================================>
c. remember <base_socket>
and REJECT connection
to signal socket.
d. LISTEN to sockets e. INIT a logger socket
<base_socket> and pair to the requestor's
<base_socket_1> to be <base_socket+2> and
connected to the created process. <base_socket+3>.
/
<==========================/
f. ACCEPT connection
with sockets from
foreign logger.
PERFORM LOGIN RITUAL
CREATED
PROCESS
-------------
g. INIT any socket pair
to requestor's
<base_socket> and
<base_socket+1>
/
<===========================/
h. ACCEPT connection
with sockets from created
process.
图 4: 在外部主机上建立一个进程
注: 本 RFC 由 Miles McCredie 于 11/99 转为机器可读形式, 以便录入在线 RFC 档案库。