如何通过CPU实现原子性?

时间:2014-08-17 14:25:33

标签: multithreading x86 cpu atomic cpu-architecture

我被告知/在线阅读缓存一致性协议MESI / MESIF:

http://en.wikipedia.org/wiki/MESI_protocol

还强制执行原子性 - 例如锁定。但是,由于以下原因,这对我来说真的没有意义:

1)MESI管理所有指令的缓存访问。如果MESI也强制执行原子性,我们如何获得竞争条件?当然所有指令都是原子的,我们永远不会遇到竞争条件?

2)如果MESI保证了原子性,那么LOCK前缀是什么意思?

3)为什么人们说原子指令带有开销 - 如果它们是使用与所有其他x86指令相同的缓存一致性模型实现的?

一般来说,有人可以解释一下CPU如何在低级别实现锁定吗?

3 个答案:

答案 0 :(得分:3)

LOCK前缀有一个目的,即锁定该地址,然后指示MESI在所有其他处理器上刷新该缓存行,以便所有其他处理器(或硬件设备!)读取或写入该地址直到锁被释放(它在指令的末尾)。

LOCK前缀很慢(几百个周期),因为它必须在一段时间内同步总线,总线速度和延迟远低于CPU速度。

LOCK指令的一般操作

1. validate
2. establish address lock on cache line
3. wait for all processors to flush (MESI kicks in here)
4. perform operation within cache line
5. flush cache line to RAM (which releases the lock)

免责声明:大部分内容来自Pentium F00F错误的文档(验证部分在建立锁定后错误地完成),因此可能已过时。

答案 1 :(得分:3)

正如@voo所说,你在混淆与原子性相混淆。

缓存一致性涵盖了许多场景,但基本示例是当两个不同的代理(多核芯片上的核心,多插槽芯片上的处理器等)访问同一行时,它们可能都在本地缓存。 MESI保证当其中一个写入新值时,所有其他过时副本首先失效,以防止使用旧值。作为副产品,这实际上保证了对高速缓存行粒度的读取或写入访问的原子性,这是x86(以及许多其他体系结构)上的CPU章程的一部分。 。它不仅如此 - 它是CPU为您提供的内存排序和一致性保证的关键部分。

但是,它并没有提供任何更大规模的原子性,这对于处理线程安全和关键部分等概念至关重要。你所指的锁定操作是一个读 - 修改 - 写流,默认情况下不保证是原子的(至少不是普通的CPU),因为它包含2个不同的内存访问。如果没有锁定,CPU可能会在其间接收到监听,并且必须根据MESI协议进行响应。以下情况对于例如:

是完全合法的
  core 0       |      core 1
---------------------------------
y = read [x]   |
increment y    |    store [x] <- z 
               |
store [x] <- y |

意味着核心0上的内存增量操作没有按预期工作。如果[x]持有互斥量,例如,你可能认为它是免费的,而你设法抓住它,而核心1已经接受它。

对核心0的读取 - 修改 - 写入操作进行了锁定(并且x86提供了许多可能的选项,锁定的add / inc,锁定的比较交换等等),会阻止其他核心直到操作完成,所以它本质上增强了核心间协议,允许拒绝窥探。

应该注意的是,如果正确使用替代保证(如栅栏),简单的MESI协议可以提供无锁方法来执行原子操作。

答案 2 :(得分:1)

我认为重点在于,虽然缓存涉及普通的内存操作,但是对于原子操作而言,需要执行 more ,而不是运行磨机操作。


稍后添加......

对于普通操作:

  • 写入内存时,典型的内核/ cpu将保持写入状态 队列,这样一旦写入已经调度,核心/ cpu 继续处理指令,而其他一些机制交易 清空挂起的写入队列 - 与之协商 根据需要缓存。在某些处理器上,挂起的写入不一定是 按顺序写下来。

  • 从内存中读取时,如果没有立即需要的值 可用,核心/ cpu可以继续处理指令,而 一些其他机制执行所需的读取 - 与...协商 根据需要缓存。

所有这些都旨在让核心/ cpu继续前进,尽可能地与真正可怕的访问真实内存的业务分离,通过缓存层,这是非常可怕的

现在,对于您的原子操作,core / cpu的状态必须与缓存/内存的状态同步。

因此,对于&#34;发布&#34; store:(a)写入队列中的所有内容必须在(b)&#34; release&#34;之前完成。写入本身完成,之前(c)正常处理可以继续。因此,在原子写入完成之前,可能必须放弃异步写入缓存/内存的所有好处。同样,对于&#34;获得&#34; load:&#34;获取&#34;之后的任何读取读必须延迟。

碰巧的是,x86表现得非常出色,并且#34;表现良好。它没有重新排序写入,所以&#34;发布&#34;商店不需要任何额外的工作来确保它在任何早期商店之后。在阅读方面,它也不需要为&#34;获取&#34;做任何特殊的事情。如果两个或多个内核/ cpus正在读取和写入同一块内存,则会有更多的缓存行无效和重新加载,并伴随着开销。做一个&#34;顺序一致&#34;在存储之后,必须执行显式mfence操作,这将使cpu / core停止,直到所有写入都从写入队列中刷新。确实&#34;顺序一致&#34;更容易思考...但对于通过锁保护对共享数据的访问的代码,&#34;获取&#34; /&#34;发布&#34;已经足够了。

对于你的原子&#34;读 - 修改 - 写&#34;及其条件版本,与高速缓存/存储器的交互甚至更强。执行操作的cpu / core必须不仅要与缓存/内存状态同步,还必须安排其他访问原子操作对象的cpus / cores停止运行,直到它完成并被写入(已提交)缓存/内存)。这种影响将取决于当时是否与其他cpu / s核心存在任何实际争用。