【转】用汇编实现原子操作

ARM下的原子操作实现原理 本文的重点是学习C内嵌汇编的语法和ldrex/strex指令。 阅读详情

 

原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断。在单处理器系统(UniProcessor)中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源 互斥的原因。

在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。

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

 

原子整数操作最常见的用途就是实现计数器。使用复杂的锁机制来保护一个单纯的计数器是很笨拙的,所以,开发者最好使用atomic_inc()和atomic_dec()这两个相对来说轻便一点的操作。
还可以用原子整数操作原子地执行一个操作并检查结果。一个常见的例子是原子的减操作和检查。
int atomic_dec_and_test(atomic_t *v)
这个函数让给定的原子变量减1,如果结果为0,就返回1;否则返回0

 

原子操作(1) - 用汇编实现原子操作

“最轻量级的锁”,通常也叫”原子操作”,之所以加引号是因为他们在汇编级别并不是原子操作,是用多条指令完成的,这些操作大多都是利用CPU支持的汇编指令.在某些构架过时的CPU体系结构上,它们应该是用比较重量级的线程内锁实现的吧(我的猜测).

最常见的原子操作有Compare and Exchange,Self Increase/Decrease等等

80486 CPU相关指令:

LOCK:这是一个指令前缀,在所对应的指令操作期间使此指令的目标操作数指定的存储区域锁定,以得到保护。

XADD:先交换两个操作数的值,再进行算术加法操作。多处理器安全,在80486及以上CPU中支持。

CMPXCHG:比较交换指令,第一操作数先和AL/AX/EAX比较,如果相等ZF置1,第二操作数赋给第一操作数,否则ZF清0,第一操作数赋给AL/AX/EAX。多处理器安全,在80486及以上CPU中支持。

XCHG:交换两个操作数,其中至少有一个是寄存器寻址.其他寄存器和标志位不受影响.

80486以上都支持这四个操作,因此当今几乎100%CPU都支持这两个指令,也能由此用标准C和C++写出一系列几乎可以跨平台的”原子操作”函数和Lock-Free数据结构和算法.

64位平台也有一系列的相关指令,当然他们也有以上提到的指令,其下的64位原子操作函数应该和32位的分开(要问为什么?我现在告诉你恐怕你印象不深,接着看这一系列吧),而道理完全一样.因此,不存在跨CPU体系结构的问题)

“原子操作”函数:

由以上提供的几个汇编指令,我们可以做出以下实现,这些都是最常用的原语.

比较后交换

long __stdcall CompareExchange(long volatile*Destination,long Exchange,long Comperand)

{

   __asm

   {

      mov     ecx, Destination;

      mov     edx, Exchange;

      mov     eax, Comperand;

      lock cmpxchg [ecx], edx;

   }

}

交换

long __stdcall Exchange(long volatile* Target,long Value)

{

   __asm

   {

      mov      ecx, Target;

      mov      edx, Value;

label:

      lock cmpxchg [ecx], edx;//加

      jnz      short label;

   }

}

自减

long __stdcall Decrement(long volatile* Addend)

{

   __asm

   {

      mov     ecx, Addend;

      mov     eax, 0FFFFFFFFh;//-1

      lock xadd [ecx], eax; //加-1

      dec     eax;

   }

}

自增

long __stdcall Increment(long volatile* Addend)

{

   __asm

   {

      mov      ecx, Addend;

      mov      eax, 1;

      lock xadd [ecx], eax; //加

      inc      eax;

   }

}

相加后交换

long __stdcall ExchangeAdd(long volatile* Addend,long Value)

{

   __asm

   {

      mov      ecx, Addend;

      mov      eax, Value;

      lock xadd [ecx], eax;

   }

}

 

 

原子操作(2) - 泛型后的原子操作

32位的数据类型有4种,但是上面的只支持long,怎么办?手工hack?太土了,当然是C++的大规模杀伤性武器template了.

同时把几个不跨平台的地方抽出来用macro表示.

目前模板还没有得到concept的支持,所以只能用boost.type_traits低级的手动判断,要求只有32位的整数类型才能实例化这些函数.

 

 

 

#include

#include

 

#define CALL_METHOD __stdcall

#define VOLATILE volatile

 

template<typename T>

T CALL_METHOD compare_exchange32(T VOLATILE*Destination,T exchange32,T Comperand)

{

   BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost ;::is_integral<T>::value);

   __asm

   {

      mov     ecx, Destination;

      mov     edx, exchange32;

      mov     eax, Comperand;

      lock cmpxchg [ecx], edx;

   }

}

 

 

 

 

template<typename T>

T CALL_METHOD exchange32(T VOLATILE* Target,T Value)

{

   BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);

   __asm

   {

      //     mov    ecx, Target;

      //     mov    edx, Value;      

      //label:

      //     lock   cmpxchg [ecx], edx;//加

      //     jnz    short label;

      mov      ecx, Target;

      mov      eax, Value;

      xchg [ecx],eax;

   }

}

 

 

 

template<typename T>

T CALL_METHOD decrement32(T VOLATILE* Addend)

{

   BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);

   __asm

   {

      mov     ecx, Addend;

      mov    eax, 0FFFFFFFFh;//-1

      lock xadd [ecx], eax; //加-1

      dec     eax;

   }

}

 

 

 

 

template<typename T>

T CALL_METHOD increment32(T VOLATILE* Addend)

{

   BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);

   __asm

   {

      mov      ecx, Addend;

      mov      eax, 1;

      lock xadd [ecx], eax; //加

      inc      eax;

   }

}

 

 

template<typename T>

T CALL_METHOD exchange_add32(T VOLATILE* Addend,T Value)

{

   BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);

   __asm

   {

      mov      ecx, Addend;

      mov      eax, Value;

      lock xadd [ecx], eax;

   }

}

 

原子操作(3) - 原子数类

根据上面的5个函数就能做出一个原子操作的整数数字类,这将是下一节中,我的最轻量级锁的基础和原型,他不依赖于操作系统,当然,所以你也可以不叫他是锁,只是一种类似锁的机制.

一切细节看源码中穿插的注释.

#ifndef __ATOM_VALUE_H__

#define __ATOM_VALUE_H__

 

#include "atom.hpp"

#include <boost/static_assert.hpp>

#include <boost/type_traits.hpp>

 

template<typename T>

class atomic_value32

{

   //恩,用boost.type_traits来保证是位的整数类型

   BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);

 

private:

   volatile T value_;

 

public:

   atomic_value32(T v = 0)

      :value_(v){}

 

   atomic_value32(atomic_value32& v){//??? 这里留给大家,我不给出源码了

   }

 

   //需要这么一个转型符号,因为大部分时候我们将atomic_value32<T>当作一个T使用

   operator T(){return exchange_add32(&value_,0);}

 

   //赋值

   atomic_value32& operator=(T v){exchange32(&value_, v);return *this;}

   atomic_value32& operator=(atomic_value32& v){exchange32(&value_, v);return *this;}

 

   //比较并交换,好像没有什么operator与之对应,就直接拿出来了

   T compare_exchange(T to_exchange, T to_compare)

   {return compare_exchange32<T>(&value_, to_exchange, to_compare);}

 

   //只提供前置,后置似乎没什么必要,我也懒得去实现了:)

   T operator++(){return increment32(&value_);}

   T operator--(){return decrement32(&value_);}

 

   //千万不能返回引用,因为线程安全考虑,

   T operator+=(T add){return exchange_add32(&value_,add);}

   T operator+=(atomic_value32& add){return exchange_add32(&value_,add);}

   T operator-=(T add){return exchange_add32(&value_,-add);}

   T operator-=(atomic_value32& add){return exchange_add32(&value_,-add);}

 

   //6个比较符号

   bool operator==(T rhs){return operator T()==rhs;}

   bool operator==(atomic_value32& rhs){return operator T()==rhs.operator T();}

   bool operator<(T rhs){return operator T()<rhs;}

   bool operator<(atomic_value32& rhs){return operator T()<rhs.operator T();}

   bool operator!=(T rhs){return !this->operator ==(rhs);}

   bool operator!=(atomic_value32& rhs){return !this->operator ==(rhs);}

   bool operator>=(T rhs){return !this->operator <(rhs);}

   bool operator>=(atomic_value32& rhs){return !this->operator <(rhs);}

   bool operator>(T rhs){return ((*this)!=(rhs)) && !((*this)<(rhs));}

   bool operator>(atomic_value32& rhs){return ((*this)!=(rhs)) && !((*this)<(rhs));}

   bool operator<=(T rhs){return !((*this)>(rhs));}

   bool operator<=(atomic_value32& rhs){return !((*this)>(rhs));}

};

 

 

 

#endif//__ATOM_VALUE_H__

 

大模型系列——Ollama简明教程 在这篇文章中,我们将了解如何使用 Ollama、如何在 Ollama 中创建自己的模型 以及如何使用 Ollama 构建聊天机器人。这是深入研究 Ollama 的第一部分,以及我所学到的有关本地 LLM 的知识以及如何将它们用于基于推理的应用程序。在这篇文章中,你将了解—— 阅读详情

相关推荐

UG二次开发避坑指南:如何正确配置Python环境让NXOpen脚本跑起来?

本文详细介绍了UG/NX二次开发中Python环境配置的全流程与NXOpen脚本调试技巧,帮助开发者避开常见陷阱。从NXOpen架构解析到Python解释器版本匹配,再到环境变量设置与路径问题处理,提供了一套经过验证的配置方案,确保脚本顺利运行。

weixin_33859135的博客 447

多线程锁详解之【原子操作】

原子操作,顾名思义它的操作具有原子性。所谓原子性就是指动作不可分割且独占数据读写。什么是操作不可分割? 我们知道系统的线程数总是大于CPU数,系统其实是把这些线程对象放到了一个队列里面,然后轮流把这些线程对象放到CPU核心上,执行对象一部分的代码,周而复始,直至所有的线程代码执行完毕。由于CPU计算速度极快,肉眼上是看不出线程的轮流执行的,但事实上每一个线程对象,确实是轮流地,断断续续地执行,不是连续的。而线程对象这种断断续续执行的特性,我们称之为线程对象的动作是可分割。 既然原子性动作不可分割,那就说明原

qq492927689的博客 1646

GraphRAG 与 HippoRAG 深度对比:在 RAGFlow 中如何选择知识图谱方案?

本文深入对比了GraphRAG与HippoRAG在RAGFlow中的知识图谱构建方案,分析了两者在技术架构、资源消耗和场景适配性上的核心差异。GraphRAG适合需要快速响应和资源有限的场景,而HippoRAG则更适用于精确关系推理的专业领域。文章还提供了RAGFlow的演进路线和选型决策框架,帮助架构师根据实际需求做出合理选择。

weixin_27889245的博客 246

每条汇编指令原子_并发原理系列六:原子操作与 x86 上的 lock 指令前缀

原子操作是不可分割的操作,在执行完毕时它不会被任何事件中断。在单处理器系统(UniProcessor,简称 UP)中,能够在单条指令中完成的操作都可以认为是原子操作,因为中断只能发生在指令与指令之间。在多处理器系统(Symmetric Multi-Processor,简称 SMP)中情况有所不同,由于系统中有多个处理器在独立的运行,即使在能单条指令中完成的操作也可能受到干扰。在所有的 X86 CP...

weixin_39633493的博客 959

企业微信会话存档SDK集成与动态库加载实战

本文详细解析了企业微信会话存档SDK的集成与动态库加载实战,重点解决Windows与Linux环境下动态库加载的差异问题。通过实战案例和优化建议,帮助开发者高效完成企业微信API集成,确保合规存档功能稳定运行。

weixin_26934905的博客 464

实用电路精讲系列---双级运放放大电路(差分放大+跟随缓冲)

本文分析了双极运放放大电路的工作原理及其典型应用。该电路结合差分放大器和跟随缓冲器,实现高精度信号放大与阻抗匹配。差分放大器通过放大差模信号并抑制共模噪声提升信噪比;跟随缓冲器则提供阻抗变换,确保信号稳定传输。两者协同工作可优化输入阻抗、增强噪声抑制并提高设计灵活性。文章还列举了10个典型应用案例,包括医疗设备、工业传感器和新能源系统等,展示了该电路在精密电子系统中的关键作用。最后通过实用电路分析,详细说明了各模块功能及设计注意事项。

weixin_40590799的博客 2951

用汇编实现原子操作

原 子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断。在单处理器系统(UniProcessor)中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源 互斥的原因。 在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。 在x86 平台上,CPU提

u011679955的博客 1625

(超详细)Scala环境搭建(scala-2.12.15、IDEA2021.1.3)

Scala环境搭建(scala-2.12.15、IDEA2021.1.3) JunLeon——go big or go home 目录 Scala环境搭建(scala-2.12.15、IDEA2021.1.3) 一、Scala概述 1、什么是Scala? 2、Scala发展史 3、Scala与Java的关系 二、Scala环境搭建 1、环境准备 2、Scala安装与配置环境变量 3、验证Scala 三、编写第一个Scala程序 1、使用IDEA创建maven工程 2、.

JunLeon的博客 3万+

c++ 嵌入汇编的方式实现int型自增

x86 平台上,使用LOCK XADDlock xaddl+m(*value)+m"1"(1)1memory在 ARM(32 位)上,使用ldrex和strexldrexresultaddstrexvaluestrexteq %1, #0strex在 ARM64(64 位)上,可以使用ldxr和stxrldxrvaluestxrvaluecbnzstxr。

www_dong的博客 485

Java服务端实现Google Pay支付功能

全网最全代码、最清晰服务端处理Google Pay,最清晰讲解Google Pay集成逻辑。

jack2350536098的博客 1万+

手把手教你YOLOv8/YOLOv11分割训练自己数据集和推理,并教你使用Labelme工具标注数据(附分割网络结构图)

YOLOv8-seg 和 YOLOv11-seg 都是一种高效实例分割算法,实例分割模型的输出是一组勾勒出图像中每个物体的遮罩或轮廓,以及每个物体的类标签和置信度分数。当你不仅需要知道物体在图像中的位置,还需要知道它们的具体形状时,实例分割就非常有用了,功能非常强大,可以应对各种视觉任务。 YOLOv11-seg 则在 YOLOv8-seg 基础上进一步魔改了模型架构,相对分割速度可能更快,其他指标性能有待测试。实例分割可以用于自动驾驶、医疗图像处理、智能农业监控等领域。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4470

1.临界区

并发是指多个线程在同时执行: 单核–(是分时执行,不是真正的同时) 多核–(在某一个时刻,会同时有多个线程再执行) 同步则是保证在并发执行的环境中各个线程可以有序的执行 分析两条线程执行下面代码。 DWORD dwVal =0; //全局变量 线程中的代码: dwVal ++; //只有一行安全吗? 对应的汇编代码: mov eax,[0x12345678] add eax,1 mov ...

My classmates 497

Vulkan入门系列0- Vulkan与OpenGL的区别

虽然Vulkan和OpenGL都是对GPU的抽象,都能达到相同的功能,但它们的设计理念不同,可以说设计差异是非常之大,由于OpenGL从1992年到现在,已经有30多年的历史,GPU功能日新月异,与当年不可同日而语,在OpenGL保持向下兼容情况下,还要在OpenGL基础上设计一套编程接口显然是不可能的,所以推出了新编程规范Vulkan。另外在一些无窗口的应用中,Vulkan可以有选择的不创建帧缓冲区,比如在数学运算,物理模拟,处理视频流的应用中并不需要窗口,所以Vulkan支持更灵活的应用场景。

GPU全栈博主 4068

[转]用汇编实现原子操作

原子操作(1) - 用汇编实现原子操作 “最轻量级的锁”,通常也叫”原子操作”,之所以加引号是因为他们在汇编级别并不是原子操作,是用多条指令完成的,这些操作大多都是利用CPU支持的汇编指令。 在某些构架过时的CPU体系结构上,它们应该是用比较重量级的线程内锁实现的吧(我的猜测)。 最常见的原子操作有Compare and Exchange,Self Increase/Decrease等等。 ...

weixin_30628801的博客 123

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

从生理到认知:科学识别与管理“力竭”状态,构建可持续高效系统

在工程实践与个人效能管理中,系统性能的边界识别与优化是关键课题。从原理上看,无论是机械系统还是生物系统,当负载持续超过其处理能力时,都会触发保护机制或进入效率衰减状态,这本质上是一种“系统告警”。在技术层面,这涉及到资源管理、状态监控与负载均衡等核心概念。其技术价值在于,通过精确识别系统的临界点(即“力竭点”),我们可以避免系统崩溃,并科学规划负载周期,从而最大化长期产出和系统健康度。这一原理广泛应用于服务器性能调优、项目管理中的敏捷迭代,以及认知科学中的注意力管理。具体到个人工作与生活中,理解“力竭”这一

weixin_34331102的博客 487

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

飞腾CPU体系结构之内存交换原子操作

飞腾CPU体系结构之内存交换原子操作 1. 内存交换原子操作 定义:修改内存单元的数据,同时返回该内存单元原来的数据。 函数声明(以内存单元长度64位为例) static inline u64 __xchg_case_64 (u64 x, volatile void *ptr) static inline u64 __xchg_case_acq_64 (u64 x, volatile void *ptr) static inline u64 __xchg_case_rel_64 (u64 x, vo

2168

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

什么是原子操作

一. 何谓"原子操作": 原子操作就是: 不可中断的一个或者一系列操作, 也就是不会被线程调度机制打断的操作, 运行期间不会有任何的上下文切换(context switch). 二. 为什么关注原子操作? 1. 如果确定某个操作是原子的, 就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁. - (巧妙的利用原子操作和实现无锁编程) 2. 借助原子操作可以实现互斥锁(mutex). (linux中的mutex_lock_t) 3. 借助互斥锁, 可以实现让更多的操作变成原子操作. 三. 单核CPU的原..

zhuiyunzhugang的博客 1万+

go 通过汇编学习atomic原子操作原理

本文从go atomic机制出发,逐步深入了解其背后原理,x86指令集LOCK、缓存行、MESI协议等。

weixin_38597669的博客 1567

原子操作汇编实现:原理、流程与代码解析

原子操作汇编实现解析 本文深入剖析了Windows平台下原子操作的汇编实现原理。通过硬件级支持(LOCK前缀、XADD/CMPXCHG指令)确保多线程环境下的数据一致性。 核心实现包括: 原子加法:通过XADD指令实现内存值与寄存器的原子交换相加 原子减法:利用取负转换后调用加法操作 比较交换:使用CMPXCHG指令实现条件更新,自带内存屏障 原子读取:巧妙复用比较交换指令保证原子性 性能分析显示XADD指令周期为10-15,CMPXCHG为15-25。典型应用场景包括自旋锁实现和无锁队列操作。优化建议强调

liulilittle的博客 898

操作系统之原子操作

原子操作是指不会被线程调度机制打断的操作; 这种操作一旦开始,就一直运行到结束,中间不会有任何线程切换。 定义 如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构,那么这个操作是一个原子(atomic)操作。 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分。将整个操作视作一个整体是原子性的核心特征。 在多进程(线程)访问共享资源时,能够确保所有其他的进程(线程)都不在同一时间内访问相同的资源。 首先处理器会自动保证基本的内

DayDayUp 1万+

ARM架构原子操作解析

本文介绍了ARM架构实现原子操作的基本原理、代码实现以及使用原子操作的一些关键问题。

baihaishizhe的博客 2078

Linux内核中的原子操作的实现

原子操作主要使用汇编语言实现,因为使用到总线锁和寄存器等操作。

陈嘉怡的专栏 2006

【转】多CPU上的原子操作

关于单CPU,多CPU上的原子操作  转自http://software.intel.com/zh-cn/blogs/2010/01/14/cpucpu/?cid=sw:prccsdn956    所谓原子操作,就是"不可中断的一个或一系列操作" 。硬件级的原子操作:    在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生

佟强 3806

原子操作笔记

原子操作是:不可中断的一个或者一系列操作,也就是不会被线程调度机制打断的操作,运行期间不会有任何的上下文切换;

xuexiwd的博客 2万+

C语言的原子操作

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34248705的博客 5493
上一篇: 【转】gcc内嵌汇编简介
下一篇: ubuntu 下不能识别 eth0 解决方法
code09
博客等级 码龄18年 31粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值