只是好奇知道哪些CPU架构支持比较和交换原子基元?
Powerpc有更强大的原语:"lwarx"和"stwcx"
lwarx从内存加载一个值但记住该位置.触及该位置的任何其他线程或CPU将导致条件存储指令"stwcx"失败.
所以lwarx/stwcx组合允许你实现原子递增/递减,比较和交换,以及更强大的原子操作,如"原子增量循环缓冲区索引"
回答这个问题的另一种更简单的方法可能是列出不支持比较和交换的多处理器平台(或者可以用来编写一个的加载链接/存储条件).
我所知道的唯一一个是PARISC,它只有一个原子清晰的单词指令.这可以用于构造互斥体(假设一个16字节边界上的字对齐).这个结构上没有CAS(与x86,ia64,ppc,sparc,mips,s390,...不同)
一些人评论/询问x86/x64上cmpxchg是否需要"锁定"前缀.多核机器的答案是肯定的.对于没有锁定的单核机器,该指令完全是原子的.
我已经有一段时间深入研究这些东西,但我似乎记得该指令在技术上是可重启的 - 它可以在飞行途中中止指令(如果它还没有任何副作用),以避免延迟中断处理长.
很抱歉很多信件.:(
x86 ISA中的几乎所有指令(除了所谓的字符串指令,可能还有其他几个),包括CMPXCHG,在unicore CPU的上下文中都是原子的.这是因为根据x86架构,CPU在每次指令执行完成后检查到达的中断,而不是在中间.结果,可以检测到中断请求,并且仅在两个连续指令的执行之间的边界上启动它的处理.由于这一点,CPU在执行单个指令期间所采用的所有存储器引用都是隔离的,并且不能被任何其他活动交错.这种行为对于unicore和多核CPU来说很常见.但是,如果在单向CPU的上下文中,系统中只有一个单元执行对存储器的访问,则在多核CPU的上下文中,系统中有多于一个单元同时执行对存储器的访问.在这种环境中,指令隔离不足以保持一致性,因为不同CPU在同一时间进行的存储器访问可以相互交错.由于此附加保护层必须应用于数据更改协议.对于x86,该层是锁定前缀,用于在系统总线上启动原子事务.
简介:使用没有锁定前缀的CMPXCHG,XADD,BTS等同步指令是安全且成本较低的,如果您确信,该指令访问的数据只能由一个内核访问.如果您对此不满意,请应用锁定前缀以通过权衡性能来提供安全性.
CPU支持硬件同步有两种主要方法:
基于原子交易.
基于缓存一致性协议.
没有人是银弹.两种方法都有其优点和缺点.
基于原子事务的方法依赖于支持存储器总线上的特殊类型的事务.在此类事务期间,只有一个连接到总线的代理(CPU核心)才有资格访问内存.结果,一方面,在原子事务处理期间由总线所有者进行的所有存储器引用被确保为单个不可中断事务.另一方面,将强制执行所有其他总线代理(CPU内核)以等待原子事务完成,以恢复访问内存的能力.无论他们想要访问哪些内存单元都没关系,即使他们想要访问原子事务期间未被总线所有者引用的内存区域.因此,大量使用锁定前缀指令会显着降低系统速度.另一方面,由于总线仲裁器根据循环调度为每个总线代理提供对总线的访问,因此可以保证每个总线代理具有相对公平的存储器访问权限,并且所有代理将是能够以同样的速度取得进步并取得进展.此外,ABA问题在原子事务的情况下发挥作用,因为其本质上,原子事务非常短(由单个指令产生的内存引用很少)并且在事务期间对内存采取的所有动作仅依赖于存储区域的值在没有考虑到帐户的情况下,两个事务之间的其他人访问了内存区域.基于原子事务的同步支持的一个很好的例子是x86架构,其中锁定前缀指令强制CPU在原子事务中执行它们.
基于高速缓存一致性协议的方法依赖于以下事实:存储器行可以在一个时刻中仅在一个L1高速缓存中被高速缓存.缓存一致性系统中的内存访问协议类似于下一个操作序列:
CPU A将存储器行X存储在L1高速缓存中.同时CPU B希望访问存储器行X.(X - > CPU A L1)
CPU B在总线上发出内存行X访问事务.(X - > CPU A L1)
所有总线代理(CPU内核)都有一个所谓的侦听代理,它侦听总线上的所有事务,并检查事务请求的内存行访问是否存储在其所有者CPU L1缓存中.因此,CPU A侦听代理检测到CPU A拥有CPU B请求的内存行.(X - > CPU A L1)
CPU A暂停CPU B发出的内存访问事务.(X - > CPU A L1)
CPU A从其L1缓存中刷新B请求的内存行.(X - >记忆)
CPU A恢复先前暂停的事务.(X - >记忆)
CPU B从存储器中提取存储器行X. (X - > CPU B L1)
感谢协议CPU核心始终访问内存中的实际数据,并严格按顺序序列化对内存的访问,一次访问.基于高速缓存一致性协议的同步支持依赖于CPU可以容易地检测到在两个时间点之间访问特定存储器行的事实.在第一次访问必须打开事务的行X的内存期间,CPU可以标记L1缓存中的内存行必须由snooping agent控制.另外,snooping代理可以在缓存行刷新期间执行检查以识别该行是否标记为控制,并且如果受控行刷新则引发内部标志.因此,如果CPU在关闭事务的内存访问期间检查内部标志,它将知道受控内存行是否能够由其他人更改并且结论是事务必须成功完成或必须被视为失败.这是LL\SC指令类实现的方式.这种方法比原子事务更简单,并且在同步中提供了更大的灵活性,因为与原子事务方法相比,可以在其上构建更多数量的不同同步原语.这种方法更具可扩展性和效率,因为它不会阻止对系统所有其他部分的内存访问.正如您所看到的,它解决了ABA问题,因为它基于内存区域访问检测的事实,而不是内存区域更改检测的值.对参与正在进行的事务的存储器区域的任何访问都将被视为事务失败.并且这可能是好的和坏的同时,因为特定的算法可能只对内存区域的值感兴趣并且不考虑该位置是由中间的某个人访问的,直到该访问改变了内存.在这种情况下,读取中间的内存值将导致错误的否定事务失败.此外,这种方法可能导致控制流量在同一存储器线上的性能大幅下降,因为它们能够不断地相互记忆存储线,并且通过这种方式防止彼此成功完成交易.这是一个非常重要的问题,因为在终端案例中它可以在活锁中转换系统.基于缓存一致性协议的同步支持通常用于RISC CPU,因为它简单灵活.但必须指出的是,英特尔也决定在x86架构中支持这种同步支持方法.去年,英特尔发布了针对x86架构的事务同步扩展,该架构将在Haswell一代英特尔处理器中实施.结果,看起来,x86将拥有最强大的同步支持,并允许系统开发人员使用这两种方法的优势.
英特尔x86有此支持.IBM在其Solaris to Linux移植指南中给出了这个示例:
bool_t My_CompareAndSwap(IN int *ptr, IN int old, IN int new) { unsigned char ret; /* Note that sete sets a 'byte' not the word */ __asm__ __volatile__ ( " lock\n" " cmpxchgl %2,%1\n" " sete %0\n" : "=q" (ret), "=m" (*ptr) : "r" (new), "m" (*ptr), "a" (old) : "memory"); return ret; }
从ARMv6体系结构开始,ARM具有LDREX/STREX指令,可用于实现原子比较交换操作.