首页 > 其他分享 >原子操作

原子操作

时间:2022-12-06 11:31:20浏览次数:54  
标签:__ int 原子 atomic 操作 CPU


1定义​​一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构。​



2简介


在 ​​多进程​​​( ​​​线程​​​)访问资源时,能够确保所有其他的进程(线程)都不在同一时间内访问相同的资源。 ​​​原子操作​​​(atomic operation)是不需要synchronized,这是Java多线程编程的老生常谈了。所谓原子操作是指不会被 ​​​线程调度​​机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。通常所说的原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么作,但并不保证)。



3特性


原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断。在单处理器系统( UniProcessor)中,能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。这也是某些CPU ​​指令系统​​​中引入了test_and_set、test_and_clear等指令用于 ​​​临界资源​​互斥的原因。但是,在对称多处理器( Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。我们以decl (递减指令)为例,这是一个典型的"读-改-写"过程,涉及两次内存访问。设想在不同CPU运行的两个进程都在递减某个计数值,可能发生的情况是:


⒈ CPU A(CPU A上所运行的进程,以下同)从内存单元把当前计数值⑵装载进它的寄存器中;


⒉ CPU B从内存单元把当前计数值⑵装载进它的寄存器中。


⒊ CPU A在它的寄存器中将计数值递减为1;


⒋ CPU B在它的寄存器中将计数值递减为1;


⒌ CPU A把修改后的计数值⑴写回内存单元。


⒍ CPU B把修改后的计数值⑴写回内存单元。


我们看到,内存里的计数值应该是0,然而它却是1。如果该计数值是一个共享资源的 ​​引用计数​​,每个进程都在递减后把该值与0进行比较,从而确定是否需要释放该共享资源。这时,两个进程都去掉了对该共享资源的引用,但没有一个进程能够释放它--两个进程都推断出:计数值是1,共享资源仍然在被使用。



4硬件支持


原子性不可能由软件单独保证--必须需要硬件的支持,因此是和架构相关的。 在x86 平台上,CPU提供了在指令执行期间对​总线​​​加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把 ​​​总线​​锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。



5Linux


-----------------------------------------------------------


原子操作大部分使用汇编语言实现,因为c语言并不能实现这样的操作。


* 在x86的原子操作实现代码中,定义了LOCK宏,这个宏可以放在随后的 ​​内联汇编​​​指令之前。如果是SMP,LOCK宏被扩展为lock指令;否则被定义为空 -- 单CPU无需防止其它CPU的干扰,锁内存 ​​​总线​​完全是在浪费时间。


#ifdef CONFIG_SMP


#define LOCK "lock ; "


#else


#define LOCK ""


#endif


* typedef struct { volatile int counter; } atomic_t;


在所有支持的 ​​体系结构​​上原子类型atomic_t都保存一个int值。在x86的某些处理器上,由于工作方式的原因,原子类型能够保证的可用范围只有24位。 volatile是一个类型描述符,要求​编译器​​不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。


* #define ATOMIC_INIT(i) { (i) }


用于在定义原子 ​​变量​​时,初始化为指定的值。如:


static atomic_t count = ATOMIC_INIT⑴;


* static __inline__ void atomic_add(int i,atomic_t *v)


将v指向的原子 ​​变量​​​加上i。该函数不关心原子 ​​​变量​​的新值,返回void类型。



6格式


在下面的实现中,使用了带有C/C++ ​​表达式​​​的 ​​​内联汇编​​代码,格式如下(参考《AT&T ASM Syntax》):


__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);


__asm__ __volatile__指示 ​​编译器​​​原封不动保留 ​​​表达式​​​中的 ​​​汇编指令​​系列,不要考虑优化处理。涉及的约束还包括:


⒈ 等号约束(=):只能用于输出操作表达式约束,说明括号内的 ​​左值​​表达式v->counter是write-only的。


⒉ 内存约束(m):表示使用不需要借助寄存器,直接使用内存方式进行输入或输出。


⒊ 立即数约束(i):表示输入 ​​表达式​​是一个立即数(整数),不需要借助任何寄存器。


⒋ 寄存器约束(r):表示使用一个 ​​通用寄存器​​,由GCC在%eax/%ax/%al、%ebx/%bx/%bl、%ecx/%cx/%cl和%edx/%dx/%dl中选取一个合适的。



7相关程序


{


__asm__ __volatile__(


LOCK "addl %1,%0"


:"=m" (v->counter)


:"ir" (i),"m" (v->counter));


}


* static __inline__ int atomic_sub_and_test(int i,atomic_t *v)


----------------------------------------


从v 指向的原子变量减去i,并测试是否为0。若为0,返回真,否则返回假。由于x86的subl指令会在结果为0时设置CPU的zero标志位,而且这个标志位是CPU私有的,不会被其它CPU影响。因此,可以执行一次加锁的减操作,再根据CPU的zero标志位来设置本地变量c,并相应返回。


{


unsigned char c;


__asm__ __volatile__(


LOCK "subl %2,%0; sete %1"


:"=m" (v->counter),"=qm" (c)


:"ir" (i),"m" (v->counter) : "memory");


return c;


}


------------------------------------


#define atomic_read(v) ((v)->counter)


读取v指向的原子 ​​变量​​的值。


#define atomic_set(v,i) (((v)->counter) = (i))


设置v指向的原子 ​​变量​​的值为i。


static __inline__ void atomic_sub(int i,atomic_t *v)


从v指向的原子变量减去i。


static __inline__ void atomic_inc(atomic_t *v)


递增v指向的原子变量。


static __inline__ void atomic_dec(atomic_t *v)


递减v指向的原子变量。


static __inline__ int atomic_dec_and_test(atomic_t *v)


递减v指向的原子变量,并测试是否为0。若为0,返回真,否则返回假。


static __inline__ int atomic_inc_and_test(atomic_t *v)


递增v指向的原子变量,并测试是否为0。若为0,返回真,否则返回假。


static __inline__ int atomic_add_negative(int i,atomic_t *v)


将v指向的原子变量加上i,并测试结果是否为负。若为负,返回真,否则返回假。这个操作用于实现semaphore。

标签:__,int,原子,atomic,操作,CPU
From: https://blog.51cto.com/u_15903730/5915599

相关文章

  • 目前各移动操作系统HTML5支持一览大图
    目前各移动操作系统HTML5支持一览大图,很详细说明小结了目前各移动操作系统对HTML5支持的情况,值得学习:[img]http://dl.iteye.com/upload/attachme......
  • 盘点JAVA中基于CAS实现的原子类, 你知道哪些?
    前言JDK中提供了一系列的基于CAS实现的原子类,CAS的全称是Compare-And-Swap,底层是lockcmpxchg指令,可以在单核和多核CPU下都能够保证比较交换的原子性。所以说,这些原子......
  • Git常用操作
    gitstash:储存当前工作区的操作,无需commitgitstashapply:恢复当前工作区储存的操作,需搭配gitstashdrop删除缓存gitstashpop:恢复当前工作区储存的操作,同时......
  • idea中你必须学会的一些基础操作
    文章目录​​1.设置​​​​2.善用Alt+Enter(简称为AE)​​​​3.创建文件时自动生成文件头部注释​​​​4.查看类的继承实现关系图​​​​5.idea中使用git管理你的代......
  • 【Elasticsearch】- 使用JavaAPI操作elasticsearch
    文章目录​​工程准备​​​​索引操作​​​​创建索引​​​​查询索引​​​​删除索引​​​​文档操作​​​​添加文档数据​​​​批量添加文档数据​​​​修改文档......
  • Flask数据库操作
    一对多fromextsimportdb#种类模型classCategroy(db.Model):id=db.Column(db.Integer,primary_key=True,autoincrement=True,comment='分类ID')name=......
  • ABAP-OpenSQL-select查询操作
    *&---------------------------------------------------------------------**&ReportZ13*&*&-----------------------------------------------------------------......
  • day37 操作bom对象,dom对象
    操作bom对象bom:浏览器对象模型window对象代表浏览器窗口 //window.alert(22)window.innerHeight//595window.innerWidth//131window.innerWidth//322......
  • 线上服务异常的定位、处理与优化的探索 - 第五章 实战操作
     线上服务紧急热部署 适用场景 线上服务器未开启、不支持、不允许热部署应用。甲方IT管理制度严谨,发版需提前申请。项目组发版出现纰漏,为减少项目影响,需要动态......
  • 一文了解 Go time 包的时间常用操作
    耐心和持久胜过激烈和狂热。哈喽大家好,我是陈明勇,今天分享的知识是Gotime包的使用。如果本文对你有帮助,不妨点个赞,如果你是Go语言初学者,不妨点个关注,一起成长一起进步,......