“目录一致性与异步权限交接把这个边界展开为可逐消息复算的协议:home收齐指定共享者的撤销确认,取回脏owner的最新数据,再等待新持有者的安装回执。升级申请途中仍可能先收到失效;所有缓存均I…”
一份写请求已经发出,并不表示其他核心已经停止读取。请求者可能先得到数据,某个旧读者却还没收到失效消息。目录协议需要把“知道谁持有副本”进一步变成“已经确认这些权限撤回”。本页把每条消息分开执行,直到能指出写权真正生效的那一步。
形式陈述
单块、单目录和可乱序消息
沿用MSI 的 I/S/M 权限与最新值交接:I 禁止访问,S 只读,M 独占读写。旧页把一次总线事务作为原子步骤;这里没有总线广播顺序,而是一个块对应一个目录控制器 home。块只有一个整数 word,每次写替换该整数,所有数据消息携带完整块。
固定
每个缓存保存 (权限,值,pending),至多有一个未完成的应用操作。远程请求带身份
home 保存内存值、共享者集合或独占 owner,以及一个活动事务。后到请求放入FIFO 队列,顺序取决于请求实际抵达 home 的次序。活动事务还保存请求身份、阶段、待确认者集合与原 owner;在收到 Installed 前不开始下一事务。不能只看 home 的 owner 字段推断缓存已经有 M:发出 GrantM 后,这个字段先指向预定接收者,安装尚可能在途。
本地操作和完整消息规则
没有 pending 时,S/M 读是本地命中;M 写也是本地命中。I 读发送 GetS,I/S 写发送 GetM,并登记 pending。请求本身没有授予任何新权限。
| home 开始处理的请求 | 当前稳定持有情况 | 先做什么 | 何时可以发授权 |
|---|---|---|---|
| GetS | 无 M | 从当前内存取值 | 立即发 GrantS |
| GetS | 某缓存持 M | 向它发 FwdS | 收到该 owner 的 Data 后 |
| GetM | 无 M | 对共享者中除请求者外的每一个发送 Inv | 等待集合变空后 |
| GetM | 某缓存持 M | 向它发 FwdM | 收到该 owner 的 Data 后 |
当共享者集合为空,GetM 的等待集合也为空,可以立即发 GrantM。请求者原来是 S 时,仍会收到完整数据;我们不使用“申请时已有数据,所以只发升级许可”的优化。
消息接收规则如下,每行中的状态改变和后续发送在同一步完成:
| 接收消息 | 必须满足的条件 | 状态更新与发出的消息 |
|---|---|---|
| 缓存收 Inv(q) | 当前为 S,可同时有自己的 pending | 变 I,清掉有效值,保留 pending,向 home 发 InvAck(q) |
| 缓存收 FwdS(q) | 当前为 M | 捕获最新块,降为 S,向 home 发 Data(q,块) |
| 缓存收 FwdM(q) | 当前为 M | 捕获最新块,变 I,向 home 发 Data(q,块) |
| home 收 InvAck(q) | q 是活动事务,发送者属于等待集合 | 删除这一发送者;集合空时发 GrantM |
| home 收 Data(q,块) | q 正在等数据,发送者是记录的原 owner | 更新内存;GetS 保留旧 owner 为共享者,GetM 清掉旧持有者;再发相应 Grant |
| 缓存收 GrantS/GrantM(q,块) | q 匹配本地 pending,权限与读写类型匹配 | 安装 S/M 与数据,完成这次读或写,清 pending,发 Installed(q) |
| home 收 Installed(q) | q 匹配活动事务,发送者就是请求者 | 结束事务,立即从队列取下一项(若有) |
发 GrantS 时,把请求者加入共享者集合;发 GrantM 时,清空共享者集合并把 owner 指向请求者。整个安装等待期仍由当前活动事务占住 home。读在安装时返回块值,写在安装 M 后原子替换块值。Installed 只是报告这一步已经发生,并不是再执行一次写。
这些规则定义的是一个保守、可完整执行的教学协议。教材中更高并发的目录协议允许多个阶段重叠,需要更多瞬态和网络条件;本页没有把那些优化省略后仍沿用它们的性能结论。
直觉
三种等待,各自排除一种风险
InvAck 回答“旧读者已经失效了吗”。等待集合最初是实际应撤销的缓存身份集合,每份确认只删除对应成员。只看收到了几条任意消息不够;消息的事务身份、发送者和种类都必须匹配。
Data 回答“最后一个写者留下的值在哪里”。旧 M 收到转发请求之前仍可本地写,所以 home 不能使用请求发出时的旧内存值。旧 owner 在处理 Fwd 的那一步捕获当时最新值,并同时失去写权;从这以后没有写者能再改掉正在交接的那份数据。
Installed 回答“下一次转发能否由新持有者处理”。即便 home 已经发出 Grant,网络仍可能把它延迟。等安装确认回来后再启动后继事务,就不会向一个尚为 I 的目标转发 owner 请求。这多一次确认的成本,换来更小的瞬态状态空间。
一次升级途中,又收到撤销
A、B 都持有 S(0),C 申请写10。home 先处理 C,于是向 A、B 发 Inv。A 随后也申请写20,它的 GetM 排在 C 后面。此时 A 不能因为“我已经申请升级”而拒绝 C 的撤销。
先让 Inv 到 A,再让 A 的 InvAck 回 home。A 变 I,但写20的 pending 还在;home 的等待集合只剩 B。此时 B 仍可本地读0,这完全合法,因为 C 尚未获得 M,也尚未完成写10。
当 B 也失效并确认后,C 才收到 GrantM,安装并写10。C 可以在 Installed 尚未回到 home 时再本地写11;home 仍不能开始 A 的请求。随后 A 的 GetM 被处理,FwdM 让 C 把11交回、变 I。A 收到11后获得 M,再完成自己的写20。
A 的旧 S 副本早已失效,所以这次授权必须有完整数据。实际写把整 word 改成20,容易掩盖一次错误的旧值传输;参考器额外在执行写之前检查授权数据确实是当前块,不能只看写后的最终值。
例子与边界
从0到20的完整账本
先让 A、B 各读一次0,等待各自事务完全结束,得到两个 S(0)。每次读有 GetS、GrantS、Installed 三条消息。之后按上一节让 C 的 GetM 先抵达,再让 A 的 GetM 排队。B 失效之后也发 GetS,排在 A 后面。
| 观察点 | A | B | C | home 内存 | 正在等待什么 |
|---|---|---|---|---|---|
| 预热完成 | S(0) | S(0) | I | 0 | 无 |
| 收到 A 的 InvAck | I,写20待办 | S(0) | I,写10待办 | 0 | B 的 InvAck |
| C 安装后又本地写11 | I,写20待办 | I,读待办 | M(11) | 0 | C 的 Installed |
| C 处理 FwdM 之后 | I,写20待办 | I,读待办 | I | 0 | 携带11的 Data |
| A 安装并写20 | M(20) | I,读待办 | I | 11 | A 的 Installed |
| B 的读结束 | S(20) | S(20) | I | 20 | 最后一个 Installed 回来后空闲 |
第四行所有缓存都 I,内存却还不是最新值。这不是丢数据:最新11已经封在不可变的 Data 消息中。异步协议的数据不变量必须把在途载体包括进来,不能逐字沿用旧原子事务末尾的“无 M 就是内存最新”。
对有
三种删掉步骤后的失败
未收齐确认就授权。 在等待集合为 {B} 时,假装它已经为空,给 C 发 GrantM 并让 C 写10。此刻 C=M(10)、B=S(0),权限已经矛盾;B 的下一次本地读还会返回旧0。这不是靠“最终消息都会到”能补救的,因为错误读取已经可以发生。
用旧内存替代脏数据。 A 持 M(11),home 仍为0,B 申请读。A 降为 S 后若 home 丢弃返回的11,改以0授权 B,就会得到 A=S(11)、B=S(0)。M 的数量检查会通过,数据检查却失败;必须同时核对权限和内容。
发出 Grant 就提前处理下一项。 A 的写请求使 home 发出 GrantM,但该消息尚未到 A;此时提前开始 B 的读,向预定 owner A 发 FwdS。任意乱序网络可以先交付 FwdS,此时 A 还是 I,处理表没有合法的供数动作。增加别的等待状态可以设计另一种协议,本页选择用 Installed 握手排除这条轨迹。
改变竞争次序,结果也会改变
若先让 A 的 GetM 抵达 home,再让 C 的 GetM 抵达,A 的20会先完成,C 随后完成10。两者结束后让 B 读,答案是10,不是原轨迹的20。网络排序改变了允许的执行次序;协议保证每次读与实际完成的写一致,并不保证由发请求的墙钟时间决定胜者。
推论与应用
按每条消息保持权限与数据
令
初态各缓存 I,内存为
在共享阶段,Inv 只减少读权限,InvAck 在失效以后才发出。home 只有逐一删完等待集合才发 GrantM,此时除请求者可能仍有 S 外,其他缓存均 I;请求者安装 M 并写入只会留下一个有效副本。GrantS 仅在没有 M 且内存已经最新时发出,不会制造读写并存。
在脏交接阶段,旧 M 可以在 Fwd 到达前继续写;处理 Fwd 时捕获的正是那时的
最后,Installed 只能由已经安装权限的请求者发出。home 收到它以后才启动下一项,所以下一次所列 owner 或共享者都已真实可处理控制消息。等待升级的缓存仍接受 Inv,消除了“握着旧 S 等新 M,同时拒绝别人撤销”的循环。以上覆盖本地操作和全部消息种类,故不变量按事件数保持。
进展与真实执行成本
再加公平交付、公平处理和有限个应用操作,每个活动事务只等有限组 InvAck、一次 Data 或一次 Installed。这些响应都不依赖另一个应用操作完成,控制消息也不会被 pending 阻塞。每个活动事务因此结束;FIFO 队列中有限个前驱依次结束,每个已到达请求最终被处理。有限缓冲可能引入额外资源等待,本证明没有覆盖;可靠也不等于存在固定网络延迟上界。
协议本体保存
完整参考器用 deque 实现 home 队列,但为任意选取交付顺序,把网络保存为列表。删除列表中间一项最坏
程序的
参考资料
- Vijay Nagarajan, Daniel J. Sorin, Mark D. Hill, David A. Wood, A Primer on Memory Consistency and Cache Coherence, 2nd ed., 2020,§§8.1–8.2.6,印刷 pp.151–161(含封面 PDF pp.173–183):目录、失效确认及瞬态;表8.1–8.2给出教材基线的完整控制器规则。本文另外选择单块串行到 Installed、数据经 home 返回的保守模型,并独立列出其完整规则与证明,没有照搬教材的并发优化表。
- 同书 §8.7.3,印刷 pp.180–182(PDF pp.202–204):去掉转发网络点到点顺序会引入的竞争,以及额外握手这一处理方向。本页依靠安装确认与串行处理允许任意消息乱序;无界缓冲是另外声明的简化,不能拿来证明真实有限互连无死锁。