一、原子变量内核操作函数

Linux中有2中原子操作: 原子变量、原子位。
原子变量的内核操作函数
原子变量的操作函数在arch/arm/include/asm/atomic.h中
原子变量类型如下,实际上就是个结构体include/linux/types.h

typedef struct {int counter;
} atomic_t

原子操作函数,如下:

函数名 作用
atomic_read(v) 读出原子变量的值,即v->counter
atomic_set(v, i) 设置原子变量的值,即v->counter = i
atomic_inc(v) v->counter++
atomic_dec(v) v->counter–
atomic_add(i, v) v->counter += i
atomic_sub(i, v) v->counter -= i
atomic_inc_and_test(v) 先加1,再判断新值是否等于0;等于0的话,返回值为1
atomic_dec_and_test(v) 先减1,再判断新值是否等于0;等于0的话,返回值为1

二、原子变量内核实现

在Linux内核文件arch\arm\include\asm\atomic.h中。
执行atomic_read、atomic_set这些操作都只需要一条汇编指令,所以它们本身就是不可打断的。
需要特别研究的是atomic_inc、atomic_dec这类读出、修改、写回的函数。
atomic_inc为例,在atomic.h文件中,如下定义:

#define atomic_inc(v)            atomic_add(1, v)

但是atomic_add在内核中是很难找到的,因为没有这个直接的声明。而是一种宏实现。
首先,在arch\arm\include\asm\atomic.h中有一个__LINUX_ARM_ARCH__判断:

当架构不同时使用不同的原子实现,armv6以下不支持SMP1结构。

所以atomic_add的原型是下面这个宏:

#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)

在ATOMIC_OPS的实现下面有一个ATOMIC_OPS(add, +=, add)的声明,把括号里面的内容替换的结果是:

/* 替换后,发现ATOMIC_OPS定义了两个函数 */
#define ATOMIC_OPS(add, +=, , add)                    \ATOMIC_OP(add, +=, , add)                    \ATOMIC_OP_RETURN(add, +=, , add)

其中ATOMIC_OP定义的内容根据armv6架构上下的区别,有不同的实现,这里只看armv6下的:

/* 所以op其实就是atomic_后面的名字, c_op是v->counter的c语言操作符,而asm_op就是汇编操作符,这里没有用到 */
#define ATOMIC_OP(op, c_op, asm_op)                 \
static inline void atomic_##op(int i, atomic_t *v)          \
{                                   \unsigned long flags;                       \\raw_local_irq_save(flags);                    \                   //关中断v->counter c_op i;                      \raw_local_irq_restore(flags);                  \               //恢复中断
}                                   \

当armv6以上时,不再是关中断(arch\arm\include\asm\atomic.h:

#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");                         \
}

① 读出:ldrex %0, [%3]

  • %0是result的值它是一个寄存器,并且带&意味着后面会被改变
  • %3是&v->counter,它是counter的地址
  • ldrex会读取&v->counter的值,放到result中,并且把&v->counter的寄存器标记为“独占访问”
    ② 修改r0的值
    ③ 写入:strex %1, %0, [%3]
  • %1 是temp的值
  • strex会去判断&v->counter如果“独占访问”还在,就把result的新值放入&v->counter,并清除“独占访问”标记
  • 如果&v->counter标记不存在了,就不会更新内存,并且把temp值设置为1,意味着失败
    ④ 判断tmp是否成功
    ⑤ 不成功,再做一次

上面的函数执行时,如果在strex之前被抢占,那么“独占访问”的标记就会被提前清除,那么最后strex执行就不成功。
这就避免的程序A、B同时修改这个变量,并且都自认为成功了。

在ARMv6以上的架构中,原子操作不再需要关闭中断,关闭中断开销太大,并且如果关闭中断,另一个CPU的中断也会运行。
新的原子操作执行过程中是可以被打断的,但是它的效果符合“原子”的定义:
一个完整的“读、修改、写入”原子的,不会被别的程序打断。
实现思路:如果被别的程序打断了,那就重来,最后总会成功的。

三、原子变量使用案例

驱动程序使用原子变量实现:只能有一个APP访问驱动程序:

static atomic_t valid = ATOMIC_INIT(1);static ssize_t gpio_key_drv_open (struct inode *node, struct file *file)
{/* armv6架构之下关中断执行 *//* armv6架构之上可以被打断,如果A中执行原子操作时,B操作了值。那么A会重新执行一次,此时值从0到-1 */if (atomic_dec_and_test(&valid))        {return 0;}atomic_inc(&valid);return -EBUSY;
}static int gpio_key_drv_close (struct inode *node, struct file *file)
{atomic_inc(&valid);return 0;
}

四、原子位

原子位操作函数在Linux内核文件arch\arm\include\asm\bitops.h中,下表中p是一个unsigned long指针。

函数名 作用
set_bit(nr, p) 设置(*p)的bit nr为1
clear_bit(nr, p) 设置(*p)的bit nr为0
change_bit(nr, p) 改变(*p)的bit nr,从1变为0,或是从0变为1
test_and_set_bit(nr, p) 设置(*p)的bit nr为1,返回该位的老值
test_and_clear_bit(nr, p) 设置(*p)的bit nr为0,返回该位的老值
test_and_change_bit(nr, p) 改变(*p)的bit nr,从1变为0,或是从0变为1;返回该位的老值

原子位内核实现

#define set_bit(nr,p)            ATOMIC_BITOP(set_bit,nr,p)
#define clear_bit(nr,p)         ATOMIC_BITOP(clear_bit,nr,p)
#define change_bit(nr,p)        ATOMIC_BITOP(change_bit,nr,p)
#define test_and_set_bit(nr,p)      ATOMIC_BITOP(test_and_set_bit,nr,p)
#define test_and_clear_bit(nr,p)    ATOMIC_BITOP(test_and_clear_bit,nr,p)
#define test_and_change_bit(nr,p)   ATOMIC_BITOP(test_and_change_bit,nr,p)

ATOMIC_BITOP也有两种实现形式:

/** The __* form of bitops are non-atomic and may be reordered.*/
#define ATOMIC_BITOP(name,nr,p)         \(__builtin_constant_p(nr) ? ____atomic_##name(nr, p) : _##name(nr,p))      //armv6以下
#else
#define ATOMIC_BITOP(name,nr,p)     _##name(nr,p)                           //armv6及以上
#endif

在armv6以下的架构中

static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{unsigned long flags;unsigned long mask = BIT_MASK(bit);p += BIT_WORD(bit);raw_local_irq_save(flags);                //关中断*p |= mask;raw_local_irq_restore(flags);          //开中断
}

在armv6及以上的架构中的实现方式(arch\arm\lib\bitops.h)
arch/arm/lib/setbit.S: bitop _set_bit, orr

#if __LINUX_ARM_ARCH__ >= 6.macro    bitop, name, instr
ENTRY(  \name       )
UNWIND( .fnstart    )ands   ip, r1, #3strneb    r1, [ip]        @ assert word-alignedmov   r2, #1and   r3, r0, #31     @ Get bit offsetmov    r0, r0, lsr #5add   r1, r1, r0, lsl #2  @ Get word offset
#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP).arch_extension   mpALT_SMP(W(pldw)   [r1])ALT_UP(W(nop))
#endifmov   r3, r2, lsl r3
1:  ldrex   r2, [r1]                            //如果被别人抢占了\instr    r2, r2, r3                          //重来一次strex r0, r2, [r1]cmp r0, #0bne   1bbx    lr
UNWIND( .fnend      )
ENDPROC(\name       ).endm

  1. :SMP就是Symmetric Multi-Processors,对称多处理器;UP即Uni-Processor,系统只有一个单核CPU。 ↩︎

rk3288 原子操作和原子位操作相关推荐

  1. C++`中的原子操作和原子类型

    5.2 C++中的原子操作和原子类型 原子操作 是个不可分割的操作. 在系统的所有线程中,你是不可能观察到原子操作完成了一半这种情况的: 它要么就是做了,要么就是没做,只有这两种可能. 如果从对象读取 ...

  2. c++ 原子操作 赋值_5.2 C++中的原子操作和原子类型

    5.2 C++中的原子操作和原子类型 原子操作 是个不可分割的操作. 在系统的所有线程中,你是不可能观察到原子操作完成了一半这种情况的: 它要么就是做了,要么就是没做,只有这两种可能. 如果从对象读取 ...

  3. 5.2 C++中的原子操作和原子类型

    5.2 C++中的原子操作和原子类型 原子操作是一类不可分割的操作,当这样操作在任意线程中进行一半的时候,你是不能查看的:它的状态要么是完成,要不就是未完成.如果从对象中读取一个值的操作是原子的,并且 ...

  4. 全局变量中断原子操作_原子操作原理分析

    原子操作原理分析 概念 原子操作是指不被打断的操作,即它是最小的执行单位.最简单的原子操作就是一条条的汇编指令(不包括一些伪指令,伪指令会被汇编器解释成多条汇编指令).在 linux 中原子操作对应的 ...

  5. linux内核同步问题

    linux内核同步问题 Linux内核设计与实现 十.内核同步方法 [手把手教Linux驱动5-自旋锁.信号量.互斥体概述]() 基础概念: 并发:多个执行单元同时进行或多个执行单元微观串行执行,宏观 ...

  6. CVTE 2017 秋季校招一面(C++ 后台)

    文章目录 0.前言 1.找出数组中第 k 大的数(手写代码) 2.从n个数中找出最小的k个数(n>>k),最优平均时间复杂度是多少 4.C 如何模拟实现 C++ 的类? 5.TCP 与 U ...

  7. 原子性操作atomic_t

    内核定义了atomic_t 数据类型,作为对整数计数器的原子操作的基础. 各个CPU平台有各自的原子操作实现方式,基本都是通过汇编实现的. 原子操作 原子操作是Linux中提供的一种实现同步的方法,所 ...

  8. lpop 原子_【concurrent】面试重灾区之原子操作你有必要了解下

    概述 在JDK1.5+的版本中,Doug Lea和他的团队还为我们提供了一套用于保证线程安全的原子操作.我们都知道在多线程环境下,对于更新对象中的某个属性.更新基本类型数据.更新数组(集合)都可能产生 ...

  9. C++11 原子类型与原子操作

    文章目录 1.认识原子操作 2.C++11 实现原子操作 3.内存模型:强顺序与弱顺序 参考文献 1.认识原子操作 原子操作是在多线程程序中"最小的且不可并行化的"操作,意味着多个 ...

最新文章

  1. 麦司机博客项目技术选型-Java后端
  2. [Java]Thinking in Java 练习2.10
  3. ubuntu查看版本及检查是否有系统更新的命令
  4. 容器技术之Dockerk8s知识笔记
  5. Linux进阶之路————scp指令介绍与演示
  6. php要懂函数吗,九个你需要知道的PHP函数和功能
  7. 2个简单shell脚本(if,while,case语句)
  8. bat自动输入密码登录_【第7期】Teamcenter自动登录改进,对portal.bat中登录密码加密...
  9. 干货收藏|如何用chrom插件实现U校园自动刷课
  10. 阿里云等企业主导的龙蜥社区发起“龙腾计划”;OpenInfra 基金会推出 LOKI 标准;GitLab 14.6 发布 | 开源日报
  11. linux下kegg注释软件,如何使用KAAS进行KEGG注释
  12. U盘装机大师安装GHOST WIN10系统
  13. 排序 ---- 快排(C语言)
  14. Android API统计
  15. php开源小程序直播,微信小程序直播
  16. 从1到100怎么做?小红书KOL五大阶段运营增长策略
  17. STM32 hal库串口空闲中断最新用法
  18. 有道云笔记markdown上传本地图片的方法
  19. VS2010版本介绍(转自:http://www.cnblogs.com/Leo_wl/archive/2010/06/02/1750035.html)
  20. 营销软文的结尾怎样写?营销软文结尾怎样去设计?

热门文章

  1. 2022年起重机械指挥考试题及模拟考试
  2. 如何学习大数据?这才是完整的大数据学习体系!!
  3. 分布式消息中间件 MetaQ 作者庄晓丹专访
  4. Google Filament 源码学习(二):三方库分类总结
  5. 上海到底是一座什么样的城市?
  6. 【知乎】如何看待同学聚会上的言论「我在四线城市跟你在一线城市的年薪一样多(15万元)」?
  7. 学习笔记-JWT 安全
  8. ESD和TVS管的区别
  9. Python ffmpeg视频压缩
  10. Java 知识点整理-7.StringBuffer类+冒泡排序+选择排序+二分法+Arrays类+基本数据类型的包装类