缓存一致性协议中消息队列的实现及消息乱序问题问询
在Paul McKenny的论文《Memory Barriers: A Hardware View for Software Hackers》3.3节中,用以下代码展示了全局内存顺序被破坏的场景:
void foo(void) { a = 1; b = 1; } void bar(void) { while (b == 0) continue; assert(a == 1); }
假设CPU0执行foo(),CPU1执行bar(),初始时a、b均为0,a的缓存行仅在CPU1缓存中,b的缓存行由CPU0拥有。执行时CPU0先发送read invalidate消息给CPU1,后响应CPU1的read消息发送b的缓存行,但CPU1先收到b的缓存行,后收到read invalidate消息,最终导致断言失败。
缓存一致性协议中消息队列的实现
每个CPU的缓存控制器会维护两类关键消息队列:
- 发送队列:缓存本地产生的、需要发送给其他缓存节点的消息,包括
read invalidate这类控制消息,以及缓存行数据传输这类数据消息。消息按操作触发顺序入队,但硬件会根据消息类型、系统性能需求动态调整发送优先级——比如优先发送能快速完成内存访问的响应消息。 - 接收队列:缓存从其他节点发来的消息,缓存控制器会按优先级调度处理:能直接满足当前CPU内存访问请求的消息会被优先处理,后台维护类的消息(如缓存行失效通知)则会延后执行。
消息传输依赖片内互连总线或专用缓存链路,不同类型的消息可能走不同物理通道,总线仲裁机制也会影响消息的传输顺序。
乱序情况的原因
场景中出现消息接收顺序反转,核心来自两方面的硬件优化:
- 传输阶段的优先级差异:
read invalidate是小体积的控制消息,而b的缓存行传输是大体积的数据响应消息。在总线仲裁时,数据响应消息(用于满足CPU1读取b的请求)通常被赋予更高优先级,会先抢占总线资源完成传输,导致它先到达CPU1的接收队列。 - 接收端的处理调度:CPU1的缓存控制器会优先处理能解除CPU指令阻塞的消息——此时CPU1正卡在
while(b==0)的循环里,接收b的缓存行后会立即更新本地缓存,让CPU看到b=1并退出循环。而read invalidate消息属于缓存一致性的后台维护操作,需要触发a的缓存行失效,优先级更低,会被延后处理。等到CPU1执行assert(a==1)时,a的缓存行仍未失效,保留初始值0,最终触发断言失败。
本质上,现代CPU为了最大化性能,允许内存操作在全局层面出现重排序,而缓存一致性协议的消息调度机制正是这种重排序的具体体现之一。
内容的提问来源于stack exchange,提问作者Weipeng
相关产品推荐
相关产品推荐

