代码之家  ›  专栏  ›  技术社区  ›  axel22

C语言中机器代码的比较与交换

  •  10
  • axel22  · 技术社区  · 14 年前

    如何用C语言编写一个函数,用嵌入式机器代码(假设x86架构)对整数值进行原子比较和交换?如果它只为i7处理器编写的话,它还能更具体些吗?

    翻译是充当一个内存围栏,还是仅仅确保排序关系就在比较和交换中包含的内存位置上?与内存围栏相比,它的成本有多高?

    谢谢您。

    5 回复  |  直到 13 年前
        1
  •  7
  •   Crashworks    14 年前

    最简单的方法可能是 compiler intrinsic 喜欢 _InterlockedCompareExchange() . 它看起来像一个函数,但实际上是编译器中的一个特例,可以归结为一个单独的机器操作。在MSVC x86内部函数的情况下,它也可以作为读/写界限,但在其他平台上不一定如此。(例如,在PowerPC上,您需要显式地发出 lwsync 限制内存重新排序。)

    一般来说,在许多常见的系统中,比较和交换操作通常只对其所接触的一个地址执行原子事务。其他的内存访问可以重新排序,在多核系统中,您交换的内存地址以外的内存地址可能在核心之间不一致。

        2
  •  7
  •   CB Bailey    14 年前

    你可以使用 CMPXCHG 使用的指令 LOCK 原子执行的前缀。

    例如。

    lock cmpxchg DWORD PTR [ebx], edx
    

    lock cmpxchgl %edx, (%ebx)
    

    这将比较EAX寄存器中的值与EBX寄存器中存储的地址处的值,并将EDX寄存器中的值存储到该位置(如果它们相同),否则它将EBX寄存器中存储的地址处的值加载到EAX中。

    您需要486或更高版本才能使用此指令。

        3
  •  4
  •   GJ.    8 年前

    如果整数值为64位,则在IA32 x86下使用cmpxchg8b 8字节比较和交换。 变量必须对齐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
    
        4
  •  3
  •   bkausbk    8 年前

    如果在原子处理器指令中省略锁前缀,则跨多处理器环境的原子操作将 不能保证 .

    在多处理器环境中,当信号被断言时,锁信号确保处理器独占使用任何共享内存。 Intel Instruction Set Reference

    如果没有锁前缀,操作将保证不会被当前处理器/内核上的任何事件(中断)中断。

        5
  •  2
  •   supercat    12 年前

    有趣的是,有些处理器不提供比较交换,而是提供一些其他指令(“加载链接”和“条件存储”),这些指令可用于合成不幸命名的比较和交换(名称听起来应该类似于“比较交换”,但实际上应该称为“比较和存储”)因为它进行比较,所以存储值是否匹配,并指示值是否匹配以及是否执行了存储)。指令不能综合比较交换语义(在比较失败时提供读取的值),但在某些情况下可以避免比较交换存在的ABA问题。许多算法都是用“CAS”操作来描述的,因为它们可以在两种类型的CPU上使用。

    一条“加载链接”指令告诉处理器读取一个内存位置,并以某种方式观察它是否可能被写入。“条件存储”指令指示处理器仅当自上次“加载链接”操作以来没有任何东西可以写入内存位置时才写入内存位置。注意,确定可能是悲观的;例如,处理中断可能会使“加载链接/条件存储”序列失效。同样地,在多处理器系统中,LL/CS序列可能被另一个CPU访问与被监视的位置在同一缓存线上的位置而失效,即使被监视的实际位置没有被触摸。在典型的用法中,LL/CS与retry循环一起使用,这样错误的失效可能会稍微慢一点,但不会造成太大的麻烦。