ARM linux 的原子操作分析
ARM linux 的原子操作分析
linux ARM的原子操作源文件位于 linux/arch/arm/include/asm/atomic.h
linux源码宏展开
最开始由如下宏定义,linux的各种宏太复杂了,分析起来有点费劲
#define ATOMIC_OP(op, c_op, asm_op) \ <-------------------|
static inline void atomic_##op(int i, atomic_t *v) \ |
{ \ |unsigned long tmp; \ |int result; \ |\ |prefetchw(&v->counter); \ |__asm__ __volatile__("@ atomic_" #op "\n" \ |
"1: ldrex %0, [%3]\n" \ |
" " #asm_op " %0, %0, %4\n" \ |
" strex %1, %0, [%3]\n" \ |
" teq %1, #0\n" \ |
" bne 1b" \ |: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) \ |: "r" (&v->counter), "Ir" (i) \ |: "cc"); \ |
} ||
#define ATOMIC_OPS(op, c_op, asm_op) \ <-----| |ATOMIC_OP(op, c_op, asm_op) \ |--------------|ATOMIC_OP_RETURN(op, c_op, asm_op) ||
ATOMIC_OPS(add, +=, add) -----------------------------------------
最终最开始的函数static inline void atomic_##op(int i, atomic_t *v)
,替换op为add, asm_op为add被声明成:
static inline void atomic_add(int i, atomic_t *v)
{ unsigned long tmp; int result;prefetchw(&v->counter); __asm__ __volatile__("@ atomic_" #op "\n"
"1: ldrex %0, [%3]\n"
" add %0, %0, %4\n"
" strex %1, %0, [%3]\n"
" teq %1, #0\n"
" bne 1b" : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter): "r" (&v->counter), "Ir" (i) : "cc");
}
又根据GCC内联汇编的替换规则,result,tmp,v->counter,&v->counter,i按照出现的顺序分别替换%0-%4,操作数被逐个替换:
static inline void atomic_add(int i, atomic_t *v)
{ unsigned long tmp; int result;prefetchw(&v->counter); __asm__ __volatile__(
"1: ldrex result, [&v->counter]\n"
" add result, result, i\n"
" strex tmp, result, [&v->counter]\n"
" teq tmp, #0\n"
" bne 1b" : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter): "r" (&v->counter), "Ir" (i) : "cc");
}
单独提取汇编部分
1:ldrex result, [&v->counter]add result, result, istrex tmp, result, [&v->counter]teq tmp, #0bne 1b
翻译成C语言:
do{result = v->counter; //ldrex result, [&v->counter]result = result + i; //add result, result, itmp = (v->counter = result赋值失败?1:0); //strex tmp, result, [&v->counter]}while(tmp != 0); //teq tmp, #0;bne 1b
比较怪的一句是tmp = (v->counter = result赋值失败?1:0);
,当然这不是合法的C语句,但是它表达的就是这个意思。它会检测v->counter = result
这句赋值操作,如果成功赋值则返回0给tmp,否则返回1。这里重点是要理解ldrex
和strex
这两条汇编是什么意思。
LDREX和STREX指令
从ARMv6架构开始,ARM处理器提供了Exclusive accesses同步原语,用于对内存访问的同步, 包含两条指令:
LDREX Rd [Rs]
STREX R0 Rd [Rs]
LDREX和STREX指令,将对一个内存地址的原子操作拆分成两个步骤,一起完成对内存的原子操作。可以理解为执行LDREX Rd [Rs]
指令会标记对[Rs]这个内存地址的访问是独占状态(exclusive state)。而执行STREX R0 Rd [Rs]
指令会让先前处于独占状态的内存地址[Rs]转变为正常状态,并且设置R0为0。若执行STREX R0 Rd [Rs]
指令时,内存地址[Rs]是正常状态,则指令的存储动作会失败,并且R0置为1。
ARM内部为了实现这个功能,还有不少复杂的情况要处理。在ARM系统中,内存有两种不同且对立的属性,即共享(Shareable)和非共享(Non-shareable)。共享意味着该段内存可以被系统中不同处理器访问到,这些处理器可以是同构的也可以是异构的。而非共享,则相反,意味着该段内存只能被系统中的一个处理器所访问到,对别的处理器来说不可见。
为了实现独占访问,ARM系统中还特别提供了所谓独占监视器(Exclusive Monitor)的东西,其结构大致如下:
这里面包含两种类型的monitor,局部和全局的。对于非共享的内存,仅关心local monitor即可,而共享的内存则要local也global都需要关心。
每个monitor是一个状态机,包含exclusive态和open态。LDREX操作会让对应的monitor变为exclusive态,而如果monitor现在为exclusive态,这时执行STREX操作会让monitor变为open态,并且存储内存这个动作执行成功,否则monitor状态不变,存储行为也失败。
UP或SMP系统中非CPU间共享的的内存原子操作
这个情况就是单个CPU上线程和线程间,或者线程和中断间的独占访问。这时关注local monitor即可。又根据文档:ARM同步原语提到:
Resetting monitors
When an operating system performs a context switch, it must reset the local monitor to open state, to prevent false positives occurring. ARMv6K introduced the Clear-Exclusive instruction, CLREX, to reset the local monitor.
在上下文切换是会通过CLREX指令将monitor复位为open态。两个线程切换执行时:
T1: Thread A调用LDREX
,导致Local Monitor变为exclusive态。
T2:系统调度触发,中断里面会调用CLREX
复位Local Monitor为open态。
T3:Thread B调用LDREX
,导致Local Monitor变为exclusive态。
T4:Thread B调用STREX
,根据状态机的切换图,Local Monitor前一状态为exclusive,则STREX
存储操作成功,并且Monitor变为open态。
T5:系统调度触发,继续复位Monitor为open态。
T6:Thead A调用STREX
,根据状态机,open态时STREX
动作会失败,存储操作失败,状态不变。
CPU线程和中断间的原子操作
T1: Thread A调用LDREX
,导致Local Monitor变为exclusive态。
T2:中断触发。
T3:中断调用LDREX
,exclusive态维持不变。
T4:中断调用STREX
,根据状态机的切换图,Local Monitor前一状态为exclusive,则STREX
存储操作成功,并且Monitor变为open态。
T5:中断返回。Thead A调用STREX
,根据状态机,open态时STREX
动作会失败,存储操作失败,状态不变。
SMP CPU间的原子操作
由于是CPU间的原子操作,那么本CPUSTREX
能否成功既要关注这个CPU的local monitor还要关注global monitor。分析上面的过程:
T1:CPU0执行LDREX
,它的local monitor由open变为exclusive态,global monitor同样由open变为exclusive态。
T2:CPU1执行LDREX
,它的local monitor由open变为exclusive态,global monitor维持exclusive态。
T3:CPU0执行STREX
,它的local/global monitor均是由exclusive态变为open态,则这次store操作成功。
T5:CPU1执行STREX
,它的local monitor由exclusive态变为open态,这是没问题的,但是它的global monitor是由open变为open态,store操作会失败,则这次STREX
的存储动作未成功。
还有其他许多复杂的可能,也可以通过ldrex/strex指令的机制分析出来。
参考资料
http://blog.chinaunix.net/uid-20543672-id-3262230.html
https://blog.csdn.net/juS3Ve/article/details/81784688
http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
https://zhuanlan.zhihu.com/p/89299392
http://blog.chinaaet.com/weiqi7777/p/5100060359
ARM linux 的原子操作分析相关推荐
- Linux内核ARM构架中原子变量的底层实现研究
前段时间重新研究了一下Linux的并发控制机制,对于内核的自旋锁.互斥锁.信号量等机制及其变体做了底层代码上的研究.因为只有从原理上理解了这些机制,在编写驱动的时候才会记得应该注意什么.这些机制基本都 ...
- I.MX6ULL ARM Linux学习笔记
I.MX6ULL ARM Linux学习笔记 写在前面 S1:U-Boot S2:正点原子U-Boot编译 S3:U-Boot基本命令第1讲:help.bdinfo和环境变量命令 S4:U-Boot基 ...
- ARM Linux中断机制分析
ARM Linux中断机制分析 --以用户模式产生irq中断为例 以下代码基于内核linux2.6.38.3(trimslice官网下载) 本文主要分析ARM发生中断时的处理流程,以在usr态发生 ...
- arm linux 启动之一:汇编启动到start_kernel
描述arm linux启动的概要过程,以S5PV210(Cortex A8)为例,本文描述第一个阶段. 一.arm linux的引导 uboot在引导arm linux(uImage镜像)到SDRAM ...
- ARM Linux 3.x的设备树(Device Tree)【转】
转自:http://blog.csdn.net/21cnbao/article/details/8457546 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ARM Devi ...
- arm linux嵌入式网络控制系统,基于ARMLinux的嵌入式网络控制系统的研究与设计
摘要: 随着计算机技术.控制技术和网络技术的蓬勃发展,嵌入式系统与网络控制系统的应用越来越广阔.当前控制系统正经历一场前所未有的变革,远程监控和智能控制成为控制系统发展的重要方向.通过嵌入式网络控制系 ...
- python linux arm_[Python]python for ARM/LINUX
按照下面的方法,可以成功将python2.5 build到 ARM /linux , 我已经成功了: Download latest from www.python.org Save into /ho ...
- 【Qt开发】【VS开发】【Linux开发】OpenCV、Qt-MinGw、Qt-msvc、VS2010、VS2015、Ubuntu Linux、ARM Linux中几个特别容易混淆的内容...
[Qt开发][VS开发][Linux开发]OpenCV.Qt-MinGw.Qt-msvc.VS2010.VS2015.Ubuntu Linux.ARM Linux中几个特别容易混淆的内容 标签:[Qt ...
- ARM linux的启动部分源代码简略分析
ARM linux的启动部分源代码简略分析 以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用. 当内核映像被加载到RAM之后,Bootloade ...
最新文章
- RHEl5 dns的配置
- 服务器负载不高 响应慢_负载均衡有哪几大类别?
- sql2000安装时报错的问题--实例挂起和267目录名无效
- 使用注解打造自己的IOC框架
- 敏捷个人架构图 V1.3
- dummy.php 下载,internal dummy connection
- 性能测试(01)-jmeter元件-线程组、调试取样器
- top命令---Linux学习笔记
- Angular里的特殊字符ɵ和ABAP变量名里的特殊字符*
- flex 瀑布流 (多列样式column布局内容被截断)
- MyBatis快速将MySQL转换成Oracle语句
- mysql 大表查询慢_mysql大表查询慢怎么优化?
- HttpReponse
- python 科学计算思维导图
- VUE Error:if there's nested data,rowKey is required错误
- ffmpeg下载及转码批量操作
- 计算机基础5y浏览器,5y浏览器
- Mac 安装VMware
- 音色、音高、音强、音长
- 本地JSON格式化工具下载
热门文章
- JS的正则表达式及详解
- Python报错:TypeError: Cannot interpret ‘1‘ as a data type
- Johnson-Trotter 生成全排列算法
- 如何转换字体为手写体?艺术字体在线生成器怎么用?
- Java中正则Matcher类的matches()、lookAt()和find()的区别
- canvas到底是块级元素还是内联元素?
- allgro pcb铜皮编辑_关于修割铜皮 - Cadence allegro PCB 教程
- java变量与常量的区别_Java变量与常量
- html样式zoom,CSS zoom属性用法及代码示例
- 原生js实现动态表格分页(封装版)