【发布时间】:2012-03-17 02:14:54
【问题描述】:
考虑以下压缩代码:
/* Compile: gcc -pthread -m32 -ansi x.c */
#include <stdio.h>
#include <inttypes.h>
#include <pthread.h>
static volatile uint64_t v = 0;
void *func (void *x) {
__sync_add_and_fetch (&v, 1);
return x;
}
int main (void) {
pthread_t t;
pthread_create (&t, NULL, func, NULL);
pthread_join (t, NULL);
printf ("v = %"PRIu64"\n", v);
return 0;
}
我有一个uint64_t 变量,我想以原子方式递增,因为该变量是多线程程序中的计数器。
为了实现原子性,我使用 GCC 的 atomic builtins。
如果我为 amd64 系统 (-m64) 编译,生成的汇编代码很容易理解。
通过使用lock addq,处理器保证增量是原子的。
400660: f0 48 83 05 d7 09 20 lock addq $0x1,0x2009d7(%rip)
但同样的 C 代码在 ia32 系统 (-m32) 上会产生非常复杂的 ASM 代码:
804855a: a1 28 a0 04 08 mov 0x804a028,%eax
804855f: 8b 15 2c a0 04 08 mov 0x804a02c,%edx
8048565: 89 c1 mov %eax,%ecx
8048567: 89 d3 mov %edx,%ebx
8048569: 83 c1 01 add $0x1,%ecx
804856c: 83 d3 00 adc $0x0,%ebx
804856f: 89 ce mov %ecx,%esi
8048571: 89 d9 mov %ebx,%ecx
8048573: 89 f3 mov %esi,%ebx
8048575: f0 0f c7 0d 28 a0 04 lock cmpxchg8b 0x804a028
804857c: 08
804857d: 75 e6 jne 8048565 <func+0x15>
这是我不明白的:
-
lock cmpxchg8b确实保证只有在预期值仍然驻留在目标地址中时才写入更改的变量。保证比较和交换以原子方式发生。 - 但是什么保证读取 0x804855a 和 0x804855f 中的变量是原子的?
是否存在“脏读”可能无关紧要,但有人可以概述一个简短的证明来证明没有问题吗?
进一步:为什么生成的代码会跳回0x8048565而不是0x804855a?我很肯定这只有在其他作者也只增加变量的情况下才是正确的。这是__sync_add_and_fetch 函数的隐含要求吗?
【问题讨论】:
标签: c gcc x86 synchronization