找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

723

积分

0

好友

97

主题
发表于 11 小时前 | 查看: 1| 回复: 0

在上一节中,我们探讨了访存时间一致性问题,其根源在于多核或多线程之间缺乏有效的互信机制。那么,硬件层面提供了哪些功能来辅助实现线程或核心之间的互信呢?

互斥访问

一种直观的思路是:共享资源被访问时,先到先得,申请到权限后其他人就无法访问,这就是最基本的锁定机制。

例如,通过一个带有 lock 属性或目的的操作指令 A 来申请总线。之后,总线将独占服务于 A,其他操作必须等待。A 完成操作后释放总线,总线才能处理其他访问。当多个操作同时竞争总线时,由总线仲裁器决定权限归属。

但这种做法会极大浪费总线带宽,即使是不访问共享区域的操作也无法被处理,显然不够高效。因此,经过优化,现代加锁机制变得更加精细,通常采用地址和长度范围结合的方式对特定区域加锁,从而释放总线资源。

有了加锁概念后,多个程序对共享变量的操作就可以用锁来控制。

以下图示说明了这一过程:

多核处理器锁机制流程图

多核系统锁通信流程图

Core0 和 Core1 会共享部分资源,我们使用 flags 作为标记来检查哪个核心获得了资源的控制权。

  • 当 Flags == 1 时,表示未上锁,两个核心都可以访问资源。
  • 当 flags 为 0 或负值时,表示已上锁,只有当前持有锁的核心可以操作。

下面给出对应的伪代码。这里的 Lock() 程序在抢不到锁时会循环检测锁变量,等待机会抢锁,这种行为被称为自旋锁(spinlock)

当多个访问者竞争资源,但只有一人能获得修改权限时,这种锁称为互斥锁

持有锁的线程在解锁前执行的操作称为临界区。临界区内的操作无需额外加锁。

看起来我们完美解决了不同线程对同一资源访问的顺序控制问题,至少避免了冲突。但这样真的没问题了吗?

我们之前提到系统中不存在零延迟传输。假设 Core0 获得锁、修改共享变量、解锁后,Core1 获得锁,但由于某些原因(如缓存延迟),Core1 可能尚未看到最新值,从而使用旧值进行操作,导致程序结果仍然错误。

也就是说,加锁只能确保共享变量被单线程修改,但无法保证修改后的数据能被正确读取。

为了解决这个问题,我们还需要另一个机制:访存屏障。

访存屏障

我们引入一类指令,要求所有特定操作或所有操作在遇到它们时必须排队,确保前面的操作全部完成。这种能让核心执行到对应操作时暂停、等待之前操作完成的指令,称为访存屏障(memory barrier)

X86 架构提供了以下指令:

  • Mfence(memory Fence):无论读写,位于 mfence 之前的所有访存指令必须全部执行并同步完成后,才执行之后的访存指令。
  • Lfence(load fence):仅针对加载指令排序。
  • Sfence(Store fence):仅针对存储指令排序。

ARM 架构也提供了类似指令:

  • DMB(data memory barrier):行为与 Mfence 类似。
  • DSB(Data synchronous barrier):行为与 DMB 类似,但时序屏障要求更严格。
  • ISB(Instruction synchronous barrier):仅当 ISB 之前的所有指令都执行且同步完成后,才操作后续指令,属于最严格的时序屏障。

此外,还有一些隐式的屏障指令,在指令行为中暗含了屏障动作,例如 X86 的 cpuid、msr 操作等。

这些是程序层面提供的手段,后续我们将探讨硬件层面的更多支持。对于更深入的计算机体系结构知识,可以参考云栈社区的相关讨论。




上一篇:腾讯元宝AI推出社交新功能“元宝派”,它和微信有何不同?
下一篇:Python实战:四种Redis部署模式(单机/主从/哨兵/集群)的连接方式详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-27 18:15 , Processed in 0.254554 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表