我有以下程序启用x86处理器标志寄存器中的对齐检查(AC)位,以捕获未对齐的内存访问.然后程序声明两个volatile变量:
#include <assert.h>
int main(void)
{
#ifndef NOASM
__asm__(
"pushf\n"
"orl $(1<<18),(%esp)\n"
"popf\n"
);
#endif
volatile unsigned char foo[] = { 1, 2, 3, 4, 5, 6 };
volatile unsigned int bar = 0xaa;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我编译它,最初生成的代码会做一些显而易见的事情,比如设置堆栈并通过将值1,2,3,4,5,6移动到堆栈上来创建字符数组:
/tmp ? gcc test3.c -m32
/tmp ? gdb ./a.out
(gdb) disassemble main
0x0804843d <+0>: push %ebp
0x0804843e <+1>: mov %esp,%ebp
0x08048440 <+3>: and $0xfffffff0,%esp
0x08048443 <+6>: sub $0x20,%esp
0x08048446 <+9>: mov %gs:0x14,%eax
0x0804844c <+15>: mov %eax,0x1c(%esp)
0x08048450 <+19>: xor %eax,%eax
0x08048452 <+21>: …Run Code Online (Sandbox Code Playgroud) 在Intel的文档称
该指令可以与
LOCK前缀一起使用,以允许指令以原子方式执行.
我的问题是
可以CMPXCHG用内存地址操作吗?从文档看来似乎没有,但任何人都可以确认只能在寄存器中使用实际的VALUE,而不是内存地址吗?
如果CMPXCHG不是原子级和高级语言级CAS必须通过LOCK CMPXCHG(带LOCK前缀)来实现,那么引入这样一条指令的目的是什么?
我找不到AtomicCmpExchange(似乎隐藏)的实现,所以我不知道它的作用.
是AtomicCmpExchange在所有平台上可靠吗?它是如何在内部实施的?它是否使用类似临界区的东西?
我有这种情况:
MainThread:
Target := 1;
Run Code Online (Sandbox Code Playgroud)
线程1:
x := AtomicCmpExchange(Target, 0, 0);
Run Code Online (Sandbox Code Playgroud)
线程2:
Target := 2;
Run Code Online (Sandbox Code Playgroud)
Thread3:
Target := 3;
Run Code Online (Sandbox Code Playgroud)
将x永远是一个整数1,2或3,或可能是别的东西吗?我的意思是,即使AtomicCmpExchange(Target, 0, 0)未能交换该值,它是否返回一个"有效"整数(我的意思是,不是半读取整数,例如,如果另一个线程已经开始写入该值的一半)?
我想避免使用临界区,我需要最大速度.
我很困惑,微软表示InterlockedExchange需要内存对齐,但是,英特尔文档说LOCK不需要内存对齐.我错过了什么,或者其他什么?谢谢
Platform SDK:DLL,进程和线程InterlockedExchange
Target参数指向的变量必须在32位边界上对齐 ; 否则,此函数将在多处理器x86系统和任何非x86系统上出现不可预测的行为.
LOCK指令在执行伴随指令期间使处理器的LOCK#信号有效(将指令转换为原子指令).在多处理器环境中,LOCK#信号确保处理器在信号被置位时独占使用任何共享存储器.
LOCK前缀的完整性不受存储器字段对齐的影响. 对于任意未对齐的字段,观察到存储器锁定.
P6中的内存排序和更新的处理器系列
锁定的指令有一个总订单.
软件控制总线锁定
总线锁的完整性不受存储器字段对齐的影响.遵循LOCK语义以获得更新整个操作数所需的多个总线周期.但是,建议锁定访问在其自然边界上对齐,以获得更好的系统性能:•8位访问的任何边界(锁定或其他).•锁定字访问的16位边界.•锁定双字访问的32位边界.•锁定四字访问的64位边界.
在Visual C++ 2013上,当我编译以下代码时
#include <atomic>
int main()
{
std::atomic<int> v(2);
return v.fetch_add(1, std::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)
我在x86上找回了以下程序集:
51 push ecx
B8 02 00 00 00 mov eax,2
8D 0C 24 lea ecx,[esp]
87 01 xchg eax,dword ptr [ecx]
B8 01 00 00 00 mov eax,1
F0 0F C1 01 lock xadd dword ptr [ecx],eax
59 pop ecx
C3 ret
Run Code Online (Sandbox Code Playgroud)
在x64上类似:
B8 02 00 00 00 mov eax,2
87 44 24 08 xchg eax,dword ptr [rsp+8]
B8 01 00 00 00 mov eax,1 …Run Code Online (Sandbox Code Playgroud) 这里这个问题表明,std::atomic<T>被普遍认为有大小相同T,而事实上,这似乎是对GCC,铛,和MSVC在x86,x64和ARM的情况。
在std::atomic<T>某个类型始终无锁的实现中T,是否保证其内存布局与的内存布局相同T?是否还有其他特殊要求std::atomic,例如对齐?
struct Data {
double a;
double b;
double c;
};
Run Code Online (Sandbox Code Playgroud)
如果在不同的线程上读取,但只有一个其他线程正在写入 a、b、c 中的每一个,那么读取每个双精度值是否正常?
如果我确保Data对齐,会出现什么情况?
struct Data {double a,b,c; } __attribute__((aligned(64));
Run Code Online (Sandbox Code Playgroud)
这将确保 a,b,c 中的每一个都对齐到 64,64+8, 64+16... 所以总是对齐到 8*8=64 位边界。
这个问题对原子 x86 指令的对齐要求及其答案让我认为Data::a/b/c从另一个线程写入并同时读取它们而不使用std::atomic.
是的,我知道std::atomic会解决这个问题,但这不是问题。
x86 ×4
atomic ×3
c++ ×3
abi ×1
assembly ×1
c ×1
c++11 ×1
compilation ×1
concurrency ×1
delphi ×1
interlocked ×1
visual-c++ ×1
winapi ×1