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指令在自旋锁中的应用