对 int 变量赋值的操作是原子的吗?
内容提要
本文讨论了原子操作和支持原子操作的指令集,单处理器系统中可在一条指令中完成,多处理器系统中需使用特定指令前缀或指令保证原子性。X86架构可使用lock指令前缀实现,ARM架构早期版本使用SWP指令,后来版本使用LDREX和STREX指令。这些指令保证内存访问的原子性。
延伸解读
单核与多核的原子性差异
文章指出,在单处理器系统中,一条指令即可视为原子操作,因为中断只发生在指令之间。但多处理器系统中,多个处理器独立运行,即使单条指令也可能被干扰,因此需要额外的原子指令或前缀。这解释了为什么简单的赋值在单核上安全,在多核上却可能出问题。
X86的lock前缀如何工作
X86通过lock指令前缀锁定前端串行总线,确保指令执行期间独占共享内存。文章引用Intel手册说明,lock前缀只能用于特定指令(如ADD、INC、XCHG等),且XCHG指令总是隐含lock。Linux内核在SMP架构下使用lock前缀,在单处理器下则忽略,体现了对多核并发的针对性处理。
ARM从关中断到独占访存的演进
早期ARM(ARMv6之前)不支持SMP,通过关闭本地中断来保护临界区。SWP指令虽能原子交换,但会阻塞所有处理器访问主存,降低多核性能。ARMv6引入LDREX和STREX独占访存指令,由exclusive monitor监视访问条件,提供更灵活、可扩展的原子更新,取代了SWP。
Q&A
在单处理器系统中,对int变量赋值是原子操作吗?
在单处理器系统中,如果赋值操作能在单条指令内完成,则被视为原子操作,因为中断只发生在指令之间。例如,X86的inc指令可以单条指令完成自增,ARM的SWP和X86的XCHG对单处理器也是原子操作。
多处理器系统中为什么单条指令操作可能不是原子的?
在多处理器系统中,多个处理器独立运行,并发的主题是处理器而非进程。即使操作在单条指令中完成,也可能受到其他处理器的干扰,因此需要额外的机制保证原子性。
X86架构如何保证多处理器环境下的原子操作?
X86使用lock指令前缀锁定前端串行总线(FSB),确保指令执行时独占共享内存,禁止其他处理器的并发访问。lock前缀只能用于特定指令(如ADD、INC、CMPXCHG等),且目标操作数必须是内存操作数。
ARMv6之前的版本如何实现原子操作?有什么局限性?
ARMv6之前通过关闭CPU中断来保护临界区,因为早期ARM不支持SMP。SWP指令用于同步而非原子操作,它会阻塞所有处理器访问主存,降低系统性能,尤其在不同频率的多核系统中。
ARMv6及以后版本引入了什么指令来支持原子操作?
ARMv6引入了LDREX和STREX指令,通过独占访存机制实现原子性。LDREX以独占方式读取内存,STREX在独占条件允许时更新内存并返回成功(0)或失败(1),提供了更灵活和可扩展的同步。
Linux内核中如何针对不同架构实现原子操作?
Linux内核通过宏LOCK_PREFIX区分架构:在SMP架构下,LOCK_PREFIX展开为lock指令前缀;在单处理器架构下,LOCK_PREFIX为空。例如,原子自增和CAS操作(cmpxchg)都使用该宏来保证原子性。