专栏首页LINUX阅码场宋宝华:关于ARM Linux原子操作的实现

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

竞态无所不在

首先我们要理解竞态(race condition)无所不在,哪怕是对一个全局变量做++的加1动作。

a=0

a++;

a++这句话,会被翻译为多条指令:

ldr r3, [r3, #0]

adds r2, r3, #1

str r2, [r3, #0]

它会先读(ldr),再修改(add),再写(str),是一个典型的读-修改-写(RMW)序列。a++在硬件上不是原子的!

假设2个线程(或者1个线程1个中断)“同时”做a++,因为加了2次,理论上a应该是等于2,但是结果a可能只是等于1,原因很简单:

假设第2个线程,在第一个线程做完读(LDR)之后,抢入率先做完a++,显然这个时候a=1,但是由于第一个线程在ldr指令里面已经读到了a=0,第1个线程在第2个线程做完a++后,继续做++还是会在0的基础上面加(只需要执行add和str指令了),所以导致第1个线程再++后,a还是等于1.

解决这样的race condition,我们需要把2个线程的a++的读-修改-写序列,串行化,彼此排他化。

也就是把这种交错的RMW:

变成这种先后发生的RMW:

这样第2个序列可以读到1,并且在1的基础上加1,保证结果是2。

LDREX和STREX

ARM V7之后的LDREX、STREX指令可以解决这个问题。它保证2个读-修改-写序列有交叉的时候,只有1个可以写成功,另外一个则再次尝试。

比如下面这个序列,R用的LDREX,W用的STREX,则只有第一个线程的STREX可以成功,而第二个的W(STREX)会失败:

类似如下:

那么,这个执行strex失败的线程2,会把第一条的LDREX指令重新执行一次:

STREX指令,除了把寄存器的值写入一个地址以外,还可以返回这次写是否成功。

STREXEQ r0, r1, [LockAddr]

上述指令把r1写入地址LockAddr,如果写入成功,则r0=0,否则r0不等于0。如果r0不等于0,证明写入失败,那么需要重新来ldrex,重新来修改和写。官方解释如下:

The STREX instruction performs a conditionalstore of a word to memory. If the exclusive monitor(s) permit thestore, the operation updates the memory location and returns the value0 in the destination register, indicating that the operation succeeded.If the exclusive monitor(s) do not permit the store, the operationdoes not update the memory location and returns the value 1 in thedestination register. This makes it possible to implement conditionalexecution paths based on the success or failure of the memory operation.For example, STREX R2, R1, [R0] performs a Store-Exclusiveoperation to the address in R0, conditionallystoring the value from R1 and indicating successor failure in R2.

类似如下流程:

当两个LDREX,STREX序列交错的时候,谁先STREX,谁成功,第2个STREX失败,类似:

所以谁先LDREX不是重点,重点是谁先STREX谁成功,后STREX的重新来LDREX。

本文分享自微信公众号 - Linux阅码场(LinuxDev),作者:宋宝华

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux内核之旅/张凯捷——系统调用分析(2)

    在《系统调用分析(1)》Linux内核之旅/张凯捷——系统调用分析(1)中,首先介绍了系统调用的概念,并对早期通过软中断(int 80)来进行系统调用的...

    Linux阅码场
  • 郭健:Linux内存管理系统参数配置之OOM(内存耗尽)

    本文是描述Linux virtual memory运行参数的第二篇,主要是讲OOM相关的参数的。为了理解OOM参数,第二章简单的描述什么是OOM。如果这个名词对...

    Linux阅码场
  • 房市静心贴:蛋蛋读NVMe之三

    Host如果想往SSD上写入用户数据,需要告诉SSD写入什么数据,写入多少数据,以及数据源在内存中的什么位置,这些信息包含在Host向SSD发送的Write命令...

    Linux阅码场
  • 聊聊flink的PartitionableListState

    flink-runtime_2.11-1.7.0-sources.jar!/org/apache/flink/runtime/state/DefaultOper...

    codecraft
  • 聊聊flink的PartitionableListState

    flink-runtime_2.11-1.7.0-sources.jar!/org/apache/flink/runtime/state/DefaultOper...

    codecraft
  • 最好的VIM/VI入门教程

    如果你已经安装了vim/vi的话,这个教程其实已经在你的电脑里了,你只需要在终端输入vimtutor即可(对于windows用户,你需要设置vim的路径到环境变...

    the5fire
  • 每天送0.2美金的小羊毛

    原文在:https://github.com/kusamanetwork/faucet/blob/master/README.md

    爬虫
  • Code.org 一个让孩子学习编程的网站

    伪君子
  • 使用python中的Numpy进行t检验

    虽然像SciPy和PyMC3这样的流行的统计数据库有预定义的函数来计算不同的测试,但是为了了解这个过程的数学原理,必须了解后台的运行。本系列将帮助你了解不同的统...

    AiTechYun
  • making Task<T> awaitable

    Eduasync part 5: making Task<T> awaitable In part 3 we looked at what the C# 5 c...

    阿新

扫码关注云+社区

领取腾讯云代金券