586以前的CPU, 会经过LOCK锁总线的形式来实现原子操做. 686开始则提供了存储一致性(Cache coherence), 这是多处理的基础, 也是原子操做的基础.git
1. 存储的粒度编程
存储的组织形式(粒度)是以CacheLine为单位的, 一般为64字节甚至更高(早期也有32字节的). 而后几组CacheLine组成一个小的LRU(或者其余替换规则).缓存
2. 协议服务器
存储一致性(CC)通常是经过MESI协议, 以及后续的变种协议, 例如Intel的MESIF协议和AMD的MOESI协议, 来实现的. 多线程
以MESI协议为例:架构
Modified: 独占CacheLine, 已经修改了, 可是还未同步到主存优化
Exclusive: 独占, 而且和主存一致线程
Shared : 共享的, 其余core也拥有该CacheLine, 而且与主存中一致server
Invalid: 表示该CacheLine不可用对象
3. 通信
有了协议, 那么就须要通信来实现协议(存储的状态). 通信有两种, 一种是广播/侦听, 一种是目录式.
广播/侦听顾名思义就是存储状态的变动, 会被广播到其余core上面去, 进而去维护CacheLine的状态. 很明显这种方式会浪费大量的流量, 并且难以扩展, CPU核数多了, 总线就是明显的瓶颈.
目录式, 就是把改变通知给具体的core, 从而避免广播. 可是考虑一种极端状况, 若是不少个core都在访问同一个CacheLine, 那仍是不能避免(事实上的)广播. 因此, 多线程编程时, 共享同一个CacheLine不是一个好的选择.
有了上面的东西, 如今咱们来考虑原子操做的实现:
1. 原子的Load/Store
因为CPU对缓存的管理是以CacheLine为单位的, 因此在一个CacheLine内load/store实际上都是原子的. Load和Store一个8字节对象, 不可能高4位和低4位是分开操做的(从而搞成俩值).
可是光有这个实际上还不够, CPU对CacheLine的修改不是当即写到主存里面去, 因此其余Core看到的值就有多是老的值, 因此这时候还须要fence来读到最新的值; 至于写, 那必定须要写权限, 即M或者E状态, 而这两个权限里面都有最新的值(只是你刚才读到的不必定是最新的, 因此有可能用老值覆盖了新值).
2. FetchAndAdd
这是比load和store稍微复杂的操做, 其实是一个复合操做. 可是有了M和E状态, 就很好理解了:
lock(CacheLine) v := load(obj) v += add store(obj, v) release(CacheLine)
x86里面是xadd指令.
3. CompareAndSwap
那么CAS, 也就能够猜出来:
lock(CacheLine) v := load(obj) if v != expected { store(obj, new_value) } release(CacheLine)
x86里面是xchg
这里说的lock和release均表示对该CacheLine独占和解出独占的意思.
关于原子操做的原理, 鲜有资料表表示其具体怎么作的, 颇有多是过于偏向于硬件. 可是对MESI等协议的思考, 实际上仍是能猜到CPU内部的实现(至少七八不离十). 好在找到两个资料, 一个是<<并行多核体系结构基础>>和<<从鲲鹏920了解现代服务器实现和引用>>. 其中鲲鹏920内存模型章节这么写到:
原子指令在软件上看来逻辑并不复杂,但在微架构上看,成本是很高的。若是咱们把CPU 和内存都看作是总线上的一个个独立的实体,有一个CPU要作CAS指令,这个CPU须要先从 内存中读一个值,同时要在内存控制器上设置一个标志,保证其余CPU写不进去,等它比 较完了,而后再决定写一个值回去,才会让其余CPU写入。
不一样微架构实现有不一样方法对行为进行优化,在鲲鹏920上,原子指令的请求须要在 L3Cache上进行排队,保证在原子操做的多个动做之间能维持原子指令要求的语义。这个 排队自己也有成本。因此没有原子须要就不要轻易用原子变量,这实际上是有成本的。
并行多核体系结构这么写到:
幸运的是, 缓存一致性协议提供了原子性被保障的基础. 举例来讲, 当遇到一个原子指令时, 这个协议知道须要保证原子性. 他首先得到对存储单元M的"独家全部权" (经过将其余包含M的缓存块中的拷贝都置为无效). 当得到独家全部权以后, 这个协议会确保只有一个处理器可以访问这个块, 而若是其余处理器在此时想要访问的话就会经历缓存缺失, 接下来原子指令就能够执行. 在原子指令持续期间, 其余处理器不容许"偷走"这个块. 距离来讲, 如通另外一个处理器要求读或者写这个块, 这个块就被"偷"了(如块被清理, 块的状态被降级为无效). 在原子指令完成以前暴露块会破坏指令的原子性, ......
参考:
1) 并行多核体系结构基础