5

我在 i686 架构中使用 cmpxchg(比较和交换)进行 32 位比较和交换,如下所示。

(编者注:最初的 32 位示例有问题,但问题不在于它。我相信这个版本是安全的,并且作为奖励,它也可以为 x86-64 正确编译。 另请注意,内联 asm 不是为此需要或推荐;或在 i486 和 x86-64 上或上__atomic_compare_exchange_n的较旧__sync_bool_compare_and_swap工作int32_tint64_t。但这个问题是关于使用内联汇编来完成的,以防万一。)

// note that this function doesn't return the updated oldVal
static int CAS(int *ptr, int oldVal, int newVal)
{
    unsigned char ret;
    __asm__ __volatile__ (
            "  lock\n"
            "  cmpxchgl %[newval], %[mem]\n"
            "  sete %0\n"
            : "=q" (ret), [mem] "+m" (*ptr), "+a" (oldVal)
            : [newval]"r" (newVal)
            : "memory");    // barrier for compiler reordering around this

    return ret;   // ZF result, 1 on success else 0
}

64 位比较和交换的 x86_64 架构的等效项是什么

static int CAS(long *ptr, long oldVal, long newVal)
{
    unsigned char ret;
    // ?
    return ret;
}
4

4 回答 4

7

x86_64指令集具有用于 8 字节(64 位)比较和交换的cmpxchgq( for quadword) 指令。q

还有一条cmpxchg8b指令适用于 8 字节数量,但设置起来更复杂,需要您使用edx:eaxecx:ebx而不是更自然的 64 位rax。几乎可以肯定,这种情况存在的原因与英特尔在很久以前x86_64就需要 64 位比较和交换操作这一事实有关。它仍然存在于 64 位模式中,但不再是唯一的选择。

但是,如前所述,cmpxchgq对于 64 位代码来说,这可能是更好的选择。


如果您需要 cmpxchg 一个 16 字节的对象,则 64 位版本cmpxchg8bcmpxchg16b. 最早的 AMD64 CPU 缺少它,因此编译器不会为 16B 对象上的std::atomic::compare_exchange生成它,除非您启用-mcx16(对于 gcc)。不过,汇编器会对其进行汇编,但请注意,您的二进制文件不会在最早的 K8 CPU 上运行。(这仅适用于cmpxchg16b,不适用于cmpxchg8b64 位模式或cmpxchgq)。

于 2009-05-07T06:23:47.643 回答
2

cmpxchg8b

__forceinline int64_t interlockedCompareExchange(volatile int64_t & v,int64_t exValue,int64_t cmpValue)
{
  __asm {
    mov         esi,v
    mov         ebx,dword ptr exValue
    mov         ecx,dword ptr exValue + 4
    mov         eax,dword ptr cmpValue
    mov         edx,dword ptr cmpValue + 4
    lock cmpxchg8b qword ptr [esi]
  }
}
于 2009-05-07T06:25:01.633 回答
1

cmpexchx64 体系结构使用良好的旧指令支持 64 位比较交换。或者您也可以使用更复杂的cmpexch8b指令(来自“ AMD64 Architecture Programmer's Manual Volume 1: Application Programming ”):

CMPXCHG指令将ALorrAX寄存器中的值与第一个(目标)操作数进行比较,并根据结果设置算术标志(ZFOFSFAFCFPF)。如果比较值相等,则将源操作数加载到目标操作数中。如果它们不相等,则将第一个操作数加载到累加器中。CMPXCHG可用于尝试拦截一个信号量,即测试其状态是否空闲,如果是,则将新值加载到信号量中,使其状态忙。测试和加载是原子执行的,因此使用信号量访问共享对象的并发进程或线程不会发生冲突。

CMPXCHG8B 指令将EDX:EAX寄存器中的 64 位值与 64 位内存位置进行比较。如果值相等,ZF则设置零标志 ( ),并将ECX:EBX值复制到内存位置。否则,ZF 清除标志,并将内存值复制到EDX:EAX.

CMPXCHG16B 指令将RDX:RAXRCX:RBX寄存器中的 128 位值与 128 位内存位置进行比较。如果值相等,ZF则设置零标志 ( ),并将RCX:RBX值复制到内存位置。否则,ZF清除标志,并将内存值复制到rDX:rAX.

如果无法推断操作数的大小,则不同的汇编语法可能需要具有指令助记符中指定的操作长度。GCC 的内联汇编器可能就是这种情况——我不知道。

于 2009-05-07T07:09:19.327 回答
-1

AMD64 架构程序员手册 V3 中 cmpxchg8B 的用法:

将 EDX:EAX 寄存器与 64 位内存位置进行比较。如果相等,则将零标志 (ZF) 设置为 1,并将 ECX:EBX 寄存器复制到内存位置。否则,将内存位置复制到 EDX:EAX 并清除零标志。

我使用 cmpxchg8B 在 x86-64 机器上实现了一个简单的互斥锁功能。这是代码

.text
.align 8
.global mutex_lock
mutex_lock:
    pushq   %rbp
    movq    %rsp,   %rbp

    jmp .L1

.L1:
    movl    $0, %edx
    movl    $0, %eax
    movl    $0, %ecx
    movl    $1, %ebx
    lock    cmpxchg8B   (%rdi)
    jne .L1
    popq    %rbp
    ret
于 2013-12-01T06:12:07.437 回答