我编译并分析了汇编输出:
struct S{
public:
int a,b,c,d,e,f,g,h,i,j,k;
};
int main() {
S s;
std::atomic<S> as;
as.store(s);
return 0;
}
我想知道它实际上是如何实现的atomic store
。对齐&#34;小&#34;它很容易。操作数。但是,现在我们有一个更广泛的操作数,所以这是一个更复杂的情况。
在我的另一个问题(Atomicity on x86)中@Peter Cordes说:
对于更广泛的操作数,例如原子地将新数据写入多个 一个结构的条目,你需要用一个锁来保护它 访问它尊重。 (您可以使用x86 lock cmpxchg16b 使用重试循环来执行原子16b存储。请注意,没有办法 在没有锁定的情况下模仿它。)
好的,但这究竟是什么意思?锁定是什么意思?
特别是,我知道lock
是一个前缀,可以确保&#34;前缀&#34;的原子性。指令。特别是@Peter Cordes说:
您可以使用x86 lock cmpxchg16b 使用重试循环来执行原子16b存储
我无法理解如何将其保持原子状态?好吧,我可以想象16B的内存块可以以原子方式存储吗?但是下一次迭代呢?
我希望我的怀疑是可以理解的,因为我有一个问题要表达它。
我正在调试上面的程序,在我的眼里,魔法在atomic_store
之后。
我想这个函数执行的是@Peter Cordes所说的。如果有人想要,我可以在这里粘贴反汇编__atomic_store
答案 0 :(得分:3)
您可以使用x86 lock cmpxchg16b和重试循环来执行原子16B存储
我真的说16b而不是16B吗?哎呀。我会将其作为更大编辑的一部分来解决。
这可以让你做一个 16B原子存储,但是它作为一个读取 - 修改 - 重写,一直保持重试,直到比较部分成功。您不能使用它以原子方式存储超过16B。
锁定是什么意思?特别是,我知道lock是一个前缀,可以确保“前缀”指令的原子性。
在spinlock / mutex中锁定,而不是lock
前缀。 lock
前缀仅适用于读 - 修改 - 写指令;没有lock mov [mem], eax
来做一个原子未对齐的商店或其他东西。 lock
ed总线周期始终是读 - 修改 - 写,正如英特尔在cmpxchg
的文档中所记录的那样。因此,lock mov
存储也会生成一个加载,如果在内存映射的I / O上使用它,它将具有不同的语义。 (读取可以触发副作用)。
我编译并分析了汇编输出...
为什么要将该代码放在main()
中,并将未初始化的垃圾从s
存储到as
?除此之外,main
在某些方面很特殊。总是更好地编写一个带有arg(或只影响全局)的函数。 atomic<s>
需要是全球性的,而不是一个可能部分优化的本地,如果你想确定你正在看到gcc“真的”做了什么。
#include <atomic>
struct S{ int a,b,c,d,e,f,g,h,i,j,k; }; // or int a[11]
std::atomic<S> as;
void atomic_struct_store_zero() {
S s = { 0 }; // initializes all members to zero
as.store(s);
}
这个compiles to函数调用__atomic_store
,传递src和dest指针以及大小。据推测它在某处使用了锁,但锁不是as
的一部分。 (sizeof(as)
== sizeof(S)
== 44
)。