1、原子操作介绍

  原子操作就是指令的以原子的方式进行,例如一条i++指令;不管该指令被反汇编成多少条指令,这些指令之间是不会被打断的。“读-修改-回写”将其三条指令绑定在一起。这样的话,如果处理器不提供新的汇编指令的话,还是用原来老的ldr,add,str这三条指令,这些指令的属性并不能保证不被打断,此时需要新的指令,这些指令在运行的时候会告诉cpu,你不能打断我的执行过程,通常这些信息我猜是保存在指令的编码中。于是armv8就提供了必要的指令集LDXR, STXR独占内存访问指令以及原子操作指令。

2、独占内存访问指令

原子操作需要处理器提供硬件上的支持,不同的处理器体系结构在原子操作上会有不同的实现。

(1)armv8提供了两种独占内存访问指令方式实现原子操作:

一种是经典的独占加载(load-exclusive)和独占存储(store-exclusive)指令,这种放方式也叫做连接加载load-link/条件存储store-conditional,也就是常说的LL/SC操作。

一种是armv8.1体系新支持的LSE指令。

(2)LL/SC操作介绍

LL/SC操作最早用于并发与同访问内存的CPU指令。LL表示从地址中读取值,并且处理器监控该值,看看其他处理器是否会修改该内存地址。SC表示如果这段时间内其他处理器没有修改该内存地址,则把新值写入该地址,如果SC失败,那么重新开始整个操作。对于ARM平台来说,也在硬件层面上提供了对LL/SC的支持,LL操作用的是LDREX指令,SC操作用的是STREX指令。在armv8后,这些指令就被改名了,修改为LDXR和STXR

(3)LDXR/STXR

ldxr指令是以内存独占加载指令,它从内存中以独占的方式加载内存地址的值到通用寄存器。这条指令能单独使用么?我的理解ldxr和stxr是成对使用的,

ldxr <xt>, [xn | sp]

stxr <ws>, <xt>, [xn, sp],执行的结果反馈在Ws寄存器中。若Ws寄存器为0, 说明ldxr和stxr指令执行完成,如果结果不为0,说明执行错误,需要重新跳到ldxr处重新执行原子加载和原子存储。

(4)LDXP/STXP--多字节独占内存访问指令

ldxp <xt1>, <xt2>, [xn | sp]

stxp <ws>, <xt1>, <xt2>, [xn | sp] 

这两条指 只不多是能够实现两个数据的同时操作罢了,没有什么特殊的。

(5)应用场景

ldxr和stxr指令可以和加载-获取,存储-释放内存屏障原语结合使用。构成一个类似临界区的内存屏障。在一些自旋锁的实现场景下非常有用。

在linux内核中提供了一些原子操作函数,这些函数的实现应该是基于处理器的实现有些不同。

学习了内嵌汇编的内容再回头看吧!!!

void atomic_add(int i, atomic_t *v)
{
	unsigned long tmp;
	int result;
	
	asm("// atomic_add\n"
	"1:ldxr%w0, [%2]\n"
	"	add%w0, %w0, %w3\n"
	"	stxr%w1, %w0, [%2]\n"
	"	cbzn%w1,  1b"
	:"=&r" (result), "=&r" (tmp)
	:"r" (&v->counter),"Ir" (i)
	:"cc");
}

常见其他原子操作函数:c++11和gcc标准中都有。

 

 

 

 

3、独占内存访问原理

内存独占访问ldxr/stxr指令是通过独占监视器来监控内存的访问的。

独占监视器

4、原子内存访问操作指令

5、比较并交互指令

6、WFE指令在自旋锁中的应用