考慮以下濃縮代碼:爲什麼__sync_add_and_fetch適用於32位系統上的64位變量?
/* 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系統上一個非常複雜的ASM代碼(-m32):
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
函數的一個暗示要求嗎?