在Linux中实现窗口功能InterlockedExchange

时间:2018-07-31 15:45:52

标签: c linux assembly

在此链接achieve GCC cas function for version 4.1.2 and earlier中,我问一个问题,要使用compare_and_swap函数来实现内置函数__sync_fetch_and_add,这是我的最终代码,可以在x86和x64上很好地运行(在 CentOS 5.0 32位 CentOS 7 64位)。


这是我的代码:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

static unsigned long  count = 0;

int sync_add_and_fetch(int* reg, int oldval, int incre) 
{
    register char result;
#ifdef __i386__
    __asm__ volatile ("lock; cmpxchgl %3, %0; setz %1" 
                     : "=m"(*reg), "=q" (result) 
                     : "m" (*reg), "r" (oldval + incre), "a" (oldval) 
                     : "memory");
    return result;
#elif defined(__x86_64__)
    __asm__ volatile ("lock; cmpxchgq %3, %0; setz %1" 
                     : "=m"(*reg), "=q" (result) 
                     : "m" (*reg), "r" (newval + incre), "a" (oldval) 
                     : "memory");
    return result;
#else
    #error:architecture not supported and gcc too old
#endif

}


void *test_func(void *arg)
{
    int i = 0;
    int result = 0;
    for(i = 0; i < 2000; ++i)
    {
        result = 0;
        while(0 == result)
        {
            result = sync_add_and_fetch((int *)&count, count, 1);
        }
    }

    return NULL;
}

int main(int argc, const char *argv[])
{
    pthread_t id[10];
    int i = 0;

    for(i = 0; i < 10; ++i){
        pthread_create(&id[i], NULL, test_func, NULL);
    }

    for(i = 0; i < 10; ++i){
        pthread_join(id[i], NULL);
    }
    //10*2000=200000
    printf("%u\n", count);

    return 0;
}

现在我还有另一个问题,就是如何在Linux InterlockedExchange中实现函数InterlockedExchange,就像上面的代码一样,分别具有__i386____x86_64__版本。只需使用参数类型不匹配上方的代码,也许汇编代码将被重写。

1 个答案:

答案 0 :(得分:0)

交换很容易;您无需返回任何状态或任何内容。它user guide总是会交换,因此您只需要返回旧值和/或状态xchg

static inline
int sync_xchg(int *p, int newval)
{
    // xchg mem,reg has an implicit lock prefix
    asm volatile("xchg %[newval], %[mem]"
        : [mem] "+m" (*p), [newval] "+r" (newval)  // read/write operands
        :
        : "memory" // compiler memory barrier, giving us seq-cst memory ordering
    );
    return newval;
}

使用int32_t可能更好,但是在所有相关的ABI中int是32位。 同样,此asm可用于任何大小的整数。 GCC将选择一个16位,32位或64位寄存器,这将暗示xchg的操作数大小。 (在没有寄存器操作数的情况下,例如addl,仅需要后缀lock; addl $1, (%0)

无论如何,我对此进行了测试,并且可以使用like for cmpxchg正确编译。我无权访问gcc4.1.1,但希望"+m"[named] asm内存操作数在该发行版中不是新的。

+m使编写防弹交换功能更加容易。例如gcc没有为相同的变量选择两个不同的存储位置作为输入vs.作为输出的风险。

gcc -O3来自gcc4.1.2的输出:

# 32-bit: -m32 -fomit-frame-pointer
sync_xchg(int*, int):
    movl    4(%esp), %edx
    movl    8(%esp), %eax
    xchg %eax, (%edx)
    ret                   # returns in EAX


# 64-bit: just -O3
sync_xchg(int*, int):
    xchg %esi, (%rdi)
    movl    %esi, %eax
    ret                    # returns in EAX

我还使用更新的gcc并单击Godbolt上的11010二进制按钮来检查该组装件是否真正组装。有趣的事实:因为GAS接受xchg mem,reg或xchg reg,mem,所以您可以使用AT&T或Intel语法(-masm=intel来编译此函数。但是,您的其他功能将无法正常工作。