如何使用嵌入式机器代码(假设,例如x86架构)在C中编写一个函数来进行原子比较和交换整数值?如果它仅针对i7处理器编写,是否可以更具体?
翻译是作为内存栅栏,还是只是确保在比较和交换中包含的内存位置上的排序关系?与记忆围栏相比,它的成本是多少?
谢谢。
答案 0 :(得分:7)
最简单的方法可能是使用compiler intrinsic _InterlockedCompareExchange()。它看起来像一个函数,但实际上是编译器中的一个特殊情况,归结为单个机器操作。在MSVC x86内在的情况下,它也可用作读/写栅栏,但在其他平台上不一定如此。 (例如,在PowerPC上,您需要明确发出lwsync来阻止内存重新排序。)
通常,在许多常见系统上,比较和交换操作通常只对其触摸的一个地址强制执行原子事务。其他内存访问可以重新排序,而在多核系统中,除了你交换的内存地址之外的内存地址可能在内核之间不一致。
答案 1 :(得分:7)
您可以使用带有CMPXCHG
前缀的LOCK
指令进行原子执行。
E.g。
lock cmpxchg DWORD PTR [ebx], edx
或
lock cmpxchgl %edx, (%ebx)
这将EAX寄存器中的值与存储在EBX寄存器中的地址的值进行比较,并将EDX寄存器中的值存储到该位置(如果它们相同),否则它会将值加载到存储在该位置的地址中。 EBX注册到EAX。
您需要有486或更高版本才能使用该指令。
答案 2 :(得分:4)
如果您的整数值是64位,那么使用cmpxchg8b进行8字节比较并在IA32 x86下进行交换。 变量必须是8字节对齐。
Example:
mov eax, OldDataA //load Old first 32 bits
mov edx, OldDataB //load Old second 32 bits
mov ebx, NewDataA //load first 32 bits
mov ecx, NewDataB //load second 32 bits
mov edi, Destination //load destination pointer
lock cmpxchg8b qword ptr [edi]
setz al //if transfer is succesful the al is 1 else 0
答案 3 :(得分:3)
如果原子处理器指令中省略了LOCK前缀,则跨多处理器环境的原子操作将无法保证。
在多处理器环境中,LOCK#信号确保处理器在声明信号时独占使用任何共享内存。 Intel Instruction Set Reference
如果没有LOCK前缀,操作将保证不会被当前处理器/内核上的任何事件(中断)中断。
答案 4 :(得分:2)
有趣的是,有些处理器不提供比较交换,而是提供一些其他指令(“加载链接”和“条件存储”),可用于合成不幸命名的比较 - 和 - swap(名称听起来应该类似于“compare-exchange”,但应该真正称为“compare-and-store”,因为它进行比较,如果值匹配则存储,并指示值是否匹配且商店是否执行)。指令无法合成比较交换语义(提供比较失败时读取的值),但在某些情况下可能会避免比较交换中出现的ABA问题。许多算法都是根据“CAS”操作来描述的,因为它们可以在两种类型的CPU上使用。
“加载链接”指令告诉处理器读取存储器位置并以某种方式观察它是否可以写入。 “条件存储”指令指示处理器仅在自上次“加载链接”操作以来没有任何内容写入时才写入存储单元。请注意,确定可能是悲观的;例如,处理中断可以使“加载链接”/“条件存储”序列无效。同样在多处理器系统中,LL / CS序列可能被另一个CPU访问到与正在观看的位置相同的高速缓存行上的位置无效,即使未被触摸的实际位置被观看也是如此。在典型的使用中,LL / CS使用非常接近,具有重试循环,因此错误的失效可能会使事情减慢一些但不会造成太大麻烦。