CAS是什么?
CAS是Compare And Set,因此CAS是一个“比较”和“交换”的复合操作。当且仅当目标内存地址的值等于“预期值”时,才将“新值”写入该地址。 “读取-比较-写入”这三个步骤必须作为一个不可分割的、连续的整体瞬间完成。
第一种CAS的实现方式
实现CAS的难点在于,先读取原本的数值,再对比是否等于预期值,最后写入新值的这个过程中,不能让其他的CPU插手。
因此一个实现CAS的方式是锁定内存总线,确保该操作在执行过程中的绝对独占性和原子性,防止其他处理器同时访问同一内存地址而导致的数据竞争和不确定性。
第二种CAS的实现方式
第一种的方式需要直接锁定内存总线,也就是在进行CAS的过程中,整个内存不可用,会阻塞所有内存访问,性能开销大。
现代多核处理器更常用缓存一致性协议(如MESI),通过缓存锁定来确保原子性,即只锁定特定缓存行,而不影响整个总线。这提高了效率,并减少了系统瓶颈。
如果目标内存区域的数据恰好缓存在当前处理器的缓存中,CPU会通过缓存一致性协议(如MESI协议)来锁定该内存地址对应的特定缓存行。它会在操作期间“持有”该缓存行,并使得其他处理器对该缓存行的访问请求无效或等待,直到当前处理器的原子操作完成。这本质上是在缓存一致性协议层面实现的、粒度更细的“锁定”,对系统整体性能影响更小。
CPU通过MESI协议为CAS操作提供底层硬件支持,核心是利用缓存行的“独占(Exclusive)”状态来实现原子性的比较与交换。
具体过程如下:
-
执行CAS指令:当
CPU核心执行一条CAS指令(如x86的CMPXCHG)时,目标是原子地更新某个内存位置的值。 -
读入与独占:
CPU会发出一个读请求,试图将目标内存地址对应的缓存行加载到自己的缓存中。为了后续能进行修改,它必须将该缓存行的状态置为 “独占(Exclusive,E)” 。这意味着:- 如果该缓存行在其他核心的缓存中是“共享(S)”状态,当前核心会通过总线发送“读无效”请求,让其他核心将其副本置为“无效(I)”。
- 只有成功获得E状态,才表示当前核心独占了这份数据,其他核心暂时无法修改它。
-
比较与交换:在独占状态下,CPU核心在缓存内部进行比较操作(比较缓存行中的当前值与指令提供的预期值)。
- 如果相等:CPU将新值写入这个处于E状态的缓存行,该行状态随之变为“已修改(M,Modified)”。这个写入操作在缓存内部完成,是原子的。
- 如果不相等:CAS操作失败,不会进行写入。核心可能会释放独占状态。
-
保证原子性:整个“读取-比较-写入”序列的原子性,关键在于MESI协议确保了在核心持有缓存行的E状态期间,其他核心无法访问或修改该数据。这相当于在缓存行级别实现了一个轻量级的“锁”,防止了多核竞争。
缓存一致性协议
缓存一致性协议是用于维护多核处理器中各个核心私有缓存数据一致性的规则集合。 它的核心目的是确保当某个核心修改了其缓存中的共享数据后,其他核心能及时感知到这一变化,从而获取到最新的数据,避免因数据不一致而导致程序运行错误。
产生这一问题的根本原因是现代CPU普遍采用了写回缓存策略和多核并行架构。当一个核心修改了缓存中的数据时,这个修改不会立即写回主内存,而是先停留在缓存中。此时,其他核心的缓存里可能还保存着这份数据的旧副本,这就造成了不一致。
最经典的缓存一致性协议是MESI协议,它通过定义缓存行的四种状态来管理一致性:
- Modified (已修改) :该缓存行中的数据已被当前核心修改,与主内存中的数据不同,且其他核心的缓存中没有副本。此核心拥有“独占”的写入权。
- Exclusive (独占) :该缓存行中的数据与主内存一致,且只存在于当前核心的缓存中。此核心可以随时修改它,修改后会变为M状态。
- Shared (共享) :该缓存行中的数据与主内存一致,但可能同时存在于多个核心的缓存中。所有核心都只能读取,不能直接修改。
- Invalid (无效) :该缓存行中的数据是无效的(已过时),不能使用。需要使用时必须从其他缓存或主内存中重新加载。