在此链接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__
版本。只需使用参数类型不匹配上方的代码,也许汇编代码将被重写。
答案 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
来编译此函数。但是,您的其他功能将无法正常工作。