简单分享记录一下最近在ARM下探索的CAS(compare and swap)实现,语言使用的是C++。我们知道ARM是RISC精简指令集,它不像x86下面有cmpxchg
这么一条指令就可以做到,我们需要用多条指令来锁内存、写入值等操作。
这里是我根据参考资料写的一个弱CAS操作函数:
|
|
GPT-4 对这段代码的理解
这段代码看起来是一个用于 ARM 架构的 CAS(Compare and Swap)操作的实现。CAS 是一种原子操作,用于在多线程环境中确保数据的一致性。这段代码使用了 ARMv8-A 架构的汇编指令。现在我来解释这段代码的每一条汇编指令:
ldaxr %w0, [%1]: 这是一条 Load-Acquire Exclusive Register 指令,将内存地址 [%1](ptr 指针所指向的内存)中的值加载到寄存器 %w0(old_value)中。这是一个独占加载操作,表示此操作会尝试获取一个独占访问权限,以确保后续的操作不会被其他处理器或内核干扰。
cmp %w0, %w2: 这是一条 Compare 指令,将寄存器 %w0(old_value)和寄存器 %w2(old_value)的值进行比较。注意这里的 %w0 和 %w2 都表示 old_value,但在第一条指令之后,%w0 的值实际上是从内存加载的值。
b.ne 1f: 这是一条 Branch 指令,当上一条 Compare 指令的结果不等(Not Equal)时,跳转到标签 1(即下面的 1: clrex)。如果从内存加载的值不等于预期的 old_value,这将跳过后续的 Store-Exclusive 操作并清除独占访问权限。
stlxr %w0, %w3, [%1]: 这是一条 Store-Exclusive Register 指令。如果独占访问权限仍然有效,那么将寄存器 %w3(new_value)的值存储到内存地址 [%1](ptr 指针所指向的内存)。同时,将存储是否成功的结果写入寄存器 %w0。如果存储成功,%w0 的值将为 0,否则为非零值。
1: clrex: 这是一条 Clear Exclusive 指令,用于清除当前处理器的独占访问权限。这样可以确保后续不再需要独占访问权限的指令不会受到影响。
这段代码看起来没有问题。当调用这个函数时,如果在内存地址 ptr 的值等于 old_value,那么将会把 new_value 存储到该内存地址,并返回原始的 old_value。如果在内存地址 ptr 的值不等于 old_value,那么什么都不会发生,直接返回从内存加载的值。
我们再来看看这段代码
有些细节是在我写这个函数的时候不太明白的,也查阅了一些资料,这里跟着说明一下。
asm后面的volatile
告诉编译器禁用对这段汇编的优化,保持原样。汇编代码结束后第一行我们定义了有哪些OutputOperands,形如“=&r”叫constraint,这里的=表明会覆盖已有变量的值,&表明这个值在指令使用完InputOperands前就会被写入,r表明使用一个通用寄存器。
这里我们没有对传入的值使用asmSymbolicName,因此每个操作数和汇编里面的从零开始的操作数一一对应:
- old_value对应了%0
- ptr对应了%1
- old_value对应了%2
- new_value对应%3
可以看到按照顺序可以对应。最后我们编译器我们改变了内存,这样编译器可以处理好读写内存的屏障。
这里和x86有一点不同,如果写入成功,我们的函数返回0,否则返回1。