当前位置: 首页 > Linux

宋宝华:关于ARMLinux原子操作的实现

时间:2023-04-07 00:37:19 Linux

本文转载,版权归作者所有。商业转载请联系作者授权,非商业转载请注明出处。作者:宋宝华来源:微信公众号linux代码阅读领域(id:linuxdev)竞态条件无处不在。首先,我们要明白竞态条件是无处不在的,哪怕是一个全局变量加1的动作。a=0a++;a++这句会被翻译成多条指令:ldrr3,[r3,#0]addsr2,r3,#1strr2,[r3,#0]会先读取(ldr),再修改(添加),然后写入(str),是一个典型的读-修改-写(RMW)序列。a++在硬件中不是原子的!假设2个线程(或1个线程和1个中断)“同时”做a++,因为加了2次,理论上a应该等于2,但结果a可能只等于1,原因很简单:假设第二个线程,第一个线程读完(LDR)后,抢第一个完成a++,显然此时a=1,但是由于第一个线程在ldr指令中读到了a=0,所以第一个线程在之后第二个线程做完a++,继续做++还是会在0上面加(只需要执行add和str指令),所以第一个线程再做完++后,a还是等于1。解决这个为racecondition,我们需要将两个线程的a++的read-modify-write序列化,让它们互斥。也就是把这种交错的RMW:改成这种连续的RMW:,这样第二个sequence就可以读到1,并且1加1保证结果为2。LDREX和STREX指令之后的LDREX和STREX指令STREXARMV7可以解决这个问题。它保证当两个读-修改-写序列相交时,只有一个能写成功,另一个会重试。比如下面的序列,R的LDREX,W的STREX,只有第一个线程的STREX可以成功,第二个W(STREX)会失败:类似下面:那么,执行失败的线程2strex,会重新执行第一条LDREX指令:STREX指令,除了可以将寄存器的值写入一个地址外,还可以返回是否写入成功。STREXEQr0,r1,[LockAddr]上面命令将r1写入地址LockAddr,如果写入成功,则r0=0,否则r0不等于0。如果r0不等于0,则证明写入成功失败,则需要重新运行ldrex修改再写入。官方解释如下:STREX指令执行一个字到内存的条件存储。如果独占监视器允许存储,则操作更新内存位置并返回目标寄存器中的值0,表示操作成功。如果独占监视器不允许存储,则该操作不会更新内存位置并在目标寄存器中返回值1。这使得可以根据内存操作的成功或失败来实施条件执行路径。例如,STREXR2,R1,[R0]对R0中的地址执行Store-Exclusive操作,有条件地存储来自R1的值并指示R2中的后继失败。类似如下过程:当两个LDREX,STREX序列交错时,谁STREXs先成功,第二个STREX失败,类似:所以谁LDREX先成功不是重点,重点是谁STREX先成功,然后再启动LDREX在STREX之后再次。更多精彩更新来袭……欢迎关注微信公众号:linux代码阅读田(id:linuxdev)