处理器如何实现原子操作

处理器如何实现原子操作

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

一、并发和并行概念

并发:单核cpu上,当多个线程在在操作时,把cpu运行时间划分为若干时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态。

并行:多核cpu上,当一个核心处理器执行一个线程时,另一个核心处理器可以同时个线程。

 

总结:

  1. 单cpu计算机中进程只能是并发,多cpu计算机中进程可以并行。
  2. 单cpu单核计算机中线程只能并发,单cpu多核计算机中线程可以并行。

 

二、存储器结构

处理器如何实现原子操作

一般而言,从高层往底层走,存储设备变得更慢、更便宜和更大。在最高层(L0),是少量快速的CPU寄存器,CPU可以在一个时钟周期内访问它们。接下来是一个或多个小型到中兴的基于SRAM的高速缓存存储器,可以在几个CPU时钟周期内访问它们。然后是一个大的基于DRAM的主存,可以在几十到几百个时钟周期内访问它们。接下来是慢速但容量很大的本地磁盘。最后,有些系统甚至包括了一层附加的远程服务器上的磁盘,要通过网络来访问他们。

 

三、多核cpu的存储关系

在多核cpu中,每个核心(core)独享L0、L1、L2存储器,共享其他的存储器。

处理器如何实现原子操作

由于存在缓存区,有可能出现缓存区数据不一致的情况。例如,有一个变量a=0,现在起两个线程都对变量a执行加一操作,假如两个线程分别在两个核心运行,开始时两个核心都把a的值0存到了自己的缓存中。线程1从自己的缓存区读取a的值0,加1后写入主内存。线程2也是从自己的缓存区读取a的值0,加1后写入主内存。最后a的值是1,而不是2。

四、实现原子操作方式

  1. 总线锁

当一个CPU核执行一个线程去访问数据做操作的时候,它会向总线上发送一个LOCK信号,此时其他的线程想要去请求主内存的时候,就会被阻塞,这样该处理器核心就可以独享这个共享内存。但其实我们只需要对a变量的操作是原子就可以了,而总线锁定把CPU和内存的通信给锁住了,使得在锁定期间,其他处理器不能操作其他内存地址的数据,从而开销较大,所以后来的CPU都提供了缓存一致性机制。

  1. 缓存一致性机制

当某核心对其缓存中的数据进行了操作之后,就通知其他核心放弃储存在它们内部的对应的缓存,或者从主内存中重新读取此数据。缓存一致性主要是通过MESI协议实现。

  1. MESI协议

MESI:缓存行(缓存的基本单位,在Intel的cpu上一般为64字节)的4种状态(Modified、Exclusive、 Share or Invalid)的缩写。该协议要求在每个缓存行上维护两个状态位,使得每个数据单位可能处于M、E、S和I这四种状态之一,协议各种状态含义如下:

M:被修改的。处于这一状态的数据,只在本CPU中有缓存数据,而其他CPU中没有。同时其状态相对于内存中的值来说,是已经被修改的,且没有更新到内存中。

E:独占的。处于这一状态的数据,只有在本CPU中有缓存,且其数据没有修改,即与内存中一致。

S:共享的。处于这一状态的数据在多个CPU中都有缓存,且与内存一致。

I:无效的。本CPU中的这份缓存已经无效

每个Core的Cache控制器不仅知道自己的读写操作,也监听其它Cache的读写操作,核心的读取会遵循几个原则:

 

一个处于M状态的缓存行,必须时刻监听所有试图读取该缓存行对应的主存地址的操作,如果监听到,则必须在此操作执行前把其缓存行中的数据写回主内存。

 

一个处于S状态的缓存行,必须时刻监听使该缓存行无效或者独享该缓存行的请求,如果监听到,则必须把其缓存行状态设置为I。

 

一个处于E状态的缓存行,必须时刻监听其他试图读取该缓存行对应的主存地址的操作,如果监听到,则必须把其缓存行状态设置为S。

 

当核心需要读取数据时,图见下方,如果其缓存行的状态是I的,则需要从内存中读取,并把自己状态变成S,如果不是I,则可以直接读取缓存中的值,但在此之前,必须要等待其他CPU的监听结果,如其他CPU也有该数据的缓存且状态是M,则需要等待其把缓存更新到内存之后,再读取。

 

当核心需要写数据时,只有在其缓存行是M或者E的时候才能执行,否则需要发出特殊的RFO指令(Read Or Ownership,这是一种总线事务),通知其他核心置缓存无效(I),这种情况下性能开销是相对较大的。在写入完成后,修改其缓存状态为M。

 

Ps:并非所有情况都会使用缓存一致性的,

  1. 被操作的数据不能被缓存在处理器内部或操作数据跨越多个缓存行,处理器会调用总线锁定
  2. 有些处理器不支持缓存行锁定,只能用总线锁定,比如说奔腾486以及更老的CPU