当前位置: 首页 > news >正文

3.2.1.2 汇编版 原子操作 CAS

基本原理说明 

x86ARM 架构上,原子操作通常利用硬件提供的原子指令来实现,比如 LOCK 前缀(x86)或 LDREX/STREX(ARM)。以下是一些关键的原子操作(例如原子递增和比较交换)的汇编实现。


1. x86 架构的原子操作

(1)原子递增

x86 的 LOCK 前缀可以用来保证指令在多核处理器上的原子性。例如原子递增操作:

实现原子递增
global atomic_increment
section .text
atomic_increment:mov eax, 1          ; 设置递增值为 1lock xadd [rdi], eax ; 对地址 rdi 所指向的变量执行原子加操作add rax, 1          ; xadd 返回的是原值,rax = 原值 + 1ret
解释:
  • LOCK XADD [rdi], eax:对 rdi 指向的地址执行原子加操作,将原值存入 eax,并把 eax 加上后写回 rdi
  • 使用 LOCK 前缀可以保证多核环境下的原子性。

(2)比较并交换(CAS)

x86 提供了 CMPXCHG 指令,可以用来实现原子的比较并交换(CAS)。

实现原子 CAS
global atomic_compare_exchange
section .text
atomic_compare_exchange:mov rax, rsi        ; 将期望值放入 raxlock cmpxchg [rdi], rdx ; 如果 [rdi] 的值等于 rax(期望值),将 rdx 写入 [rdi]ret
参数说明:
  • rdi:目标地址。
  • rsi:期望值。
  • rdx:需要交换的值。
解释:
  • LOCK CMPXCHG [rdi], rdx
    • 如果 [rdi] 的值等于 RAX,则将 rdx 写入 [rdi]
    • 否则将 [rdi] 的当前值写入 RAX
  • 返回值:RAX 中存储的是 rdi 的旧值,调用者可以根据是否等于 rsi 判断交换是否成功。

2. ARM 架构的原子操作

ARM 使用 LDREX/STREX 指令对原子操作进行支持。这是一种 负载-存储排他模型,可以保证在加载到存储之间没有其他线程或进程修改目标变量。


(1)原子递增

在 ARM 架构下,使用 LDREX/STREX 实现原子递增:

实现原子递增
.global atomic_increment
atomic_increment:mov r1, #1          ; 将 1 存入 r1(递增值)
1:ldrex r0, [r0]      ; 使用 LDREX 加载 [r0] 的值到 r0add r0, r0, r1      ; r0 = r0 + 1strex r2, r0, [r0]  ; 尝试将 r0 写回 [r0],如果成功 r2 = 0cmp r2, #0          ; 检查写入是否成功bne 1b              ; 如果不成功,重试dmb                 ; 数据内存屏障,确保操作完成bx lr
解释:
  • LDREX:加载目标地址的值,同时标记为“独占”访问。
  • STREX:尝试存储值到目标地址,如果目标地址在此期间被其他核修改,存储操作会失败,返回非 0。
  • 使用循环保证操作成功。

(2)比较并交换(CAS)

实现原子 CAS
.global atomic_compare_exchange
atomic_compare_exchange:ldrex r0, [r1]      ; 从地址 r1 加载值到 r0(旧值)cmp r0, r2          ; 比较 r0 和 r2(期望值)bne 1f              ; 如果不相等,跳转到 1f,返回旧值strex r3, r3, [r1]  ; 尝试将 r3 写入 [r1],如果成功 r3 = 0cmp r3, #0          ; 检查是否存储成功bne atomic_compare_exchange ; 如果失败,重新尝试
1:dmb                 ; 数据内存屏障,保证一致性bx lr
参数说明:
  • r1:目标地址。
  • r2:期望值。
  • r3:要交换的值。
解释:
  • 如果 LDREX 加载的值与 期望值(r2)相等,则尝试存储 r3
  • 如果存储失败,则重复尝试。
  • 成功后返回原值。

3. 汇编操作的注意事项

  1. x86 的强内存模型
    • x86 硬件有一个相对强的内存序,很多情况下 LOCK 前缀可以满足原子操作需求。
  2. ARM 的弱内存模型
    • ARM 是弱内存模型,使用 LDREX/STREX 操作时,通常需要内存屏障(如 DMB)来确保指令顺序和数据同步。
  3. 重试机制
    • 对于 ARM 的 LDREX/STREX 模式,如果存储失败需要通过循环重试。
  4. 指令可用性
    • 需要确保目标平台支持这些指令。比如 CMPXCHG 在早期的 x86 处理器(如 80386)上不可用。

4. 比较总结

操作x86ARM
原子递增LOCK XADDLDREX + ADD + STREX
原子比较交换LOCK CMPXCHGLDREX + CMP + STREX
内存屏障MFENCE(可选)DMB

x86 汇编通常较为简单,因为其强内存序模型降低了编程复杂性;ARM 则需要更明确的同步和屏障操作。

代码实现

my_atomic.h

#include <stdint.h>
#include <stdio.h>// 平台区分
#if defined(__x86_64__)#define ATOMIC_X86
#elif defined(__aarch64__)#define ATOMIC_ARM
#else#error "Unsupported platform"
#endif// ===================== x86 实现 =====================
#ifdef ATOMIC_X86// 原子递增
static inline int atomic_increment(int *addr) {int result;__asm__ __volatile__("lock xaddl %0, %1": "=r"(result), "+m"(*addr): "0"(1): "memory");return result + 1; // 返回递增后的值
}// 原子递减
static inline int atomic_decrement(int *addr) {int result;__asm__ __volatile__("lock xaddl %0, %1": "=r"(result), "+m"(*addr): "0"(-1) // 减 1: "memory");return result - 1; // 返回递减后的值
}// 原子比较交换
static inline int atomic_compare_exchange(int *addr, int expected, int desired) {int old;__asm__ __volatile__("lock cmpxchgl %2, %1": "=a"(old), "+m"(*addr): "r"(desired), "0"(expected): "memory");return old; // 返回旧值
}// 原子读取
static inline int atomic_load(int *addr) {int value;__asm__ __volatile__("movl %1, %0": "=r"(value): "m"(*addr): "memory");return value; // 返回读取值
}#endif // ATOMIC_X86// ===================== ARM 实现 =====================
#ifdef ATOMIC_ARM// 原子递增
static inline int atomic_increment(int *addr) {int old, tmp;do {__asm__ __volatile__("ldrex %0, [%2]      \n" // 读取旧值到 old"add   %1, %0, #1    \n" // tmp = old + 1"strex %0, %1, [%2]  \n" // 尝试写回: "=&r"(old), "=&r"(tmp): "r"(addr): "memory", "cc");} while (old != 0); // 如果写失败,重试return tmp; // 返回递增后的值
}// 原子递减
static inline int atomic_decrement(int *addr) {int old, tmp;do {__asm__ __volatile__("ldrex %0, [%2]      \n" // 读取旧值到 old"sub   %1, %0, #1    \n" // tmp = old - 1"strex %0, %1, [%2]  \n" // 尝试写回: "=&r"(old), "=&r"(tmp): "r"(addr): "memory", "cc");} while (old != 0); // 如果写失败,重试return tmp; // 返回递减后的值
}// 原子比较交换
static inline int atomic_compare_exchange(int *addr, int expected, int desired) {int old, status;do {__asm__ __volatile__("ldrex %0, [%2]       \n" // 加载到 old"cmp   %0, %3         \n" // 比较 old 和 expected"bne   1f             \n" // 如果不相等,跳转到 1"strex %1, %4, [%2]   \n" // 尝试写回 desired"1:": "=&r"(old), "=&r"(status): "r"(addr), "r"(expected), "r"(desired): "memory", "cc");} while (status != 0); // 如果写失败,重试return old; // 返回旧值
}// 原子读取
static inline int atomic_load(int *addr) {int value;__asm__ __volatile__("ldrex %0, [%1] \n" // 加载值到 value: "=&r"(value): "r"(addr): "memory");return value; // 返回读取值
}#endif // ATOMIC_ARM

my_atomic_test.c

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "my_atomic.h"#define NUM_THREADS 4
#define ITERATIONS 1000volatile int shared_counter = 0; // 全局共享计数器// 线程函数:执行递增操作
void* thread_increment(void* arg) {for (int i = 0; i < ITERATIONS; i++) {atomic_increment((int*)&shared_counter);}return NULL;
}// 线程函数:执行递减操作
void* thread_decrement(void* arg) {for (int i = 0; i < ITERATIONS; i++) {atomic_decrement((int*)&shared_counter);}return NULL;
}int main() {pthread_t threads[NUM_THREADS];// 创建增量线程for (int i = 0; i < NUM_THREADS / 2; i++) {if (pthread_create(&threads[i], NULL, thread_increment, NULL) != 0) {perror("pthread_create");exit(EXIT_FAILURE);}}// 创建减量线程for (int i = NUM_THREADS / 2; i < NUM_THREADS; i++) {if (pthread_create(&threads[i], NULL, thread_decrement, NULL) != 0) {perror("pthread_create");exit(EXIT_FAILURE);}}// 等待所有线程完成for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}// 最终结果int final_value = atomic_load((int*)&shared_counter);printf("最终计数器值: %d\n", final_value);return 0;
}

ubuntu x86编译运行

$ gcc -pthread -o a.out my_atomic_test.c
$ ./a.out
最终计数器值: 0


gitlab.0voice.com

相关文章:

3.2.1.2 汇编版 原子操作 CAS

基本原理说明 在 x86 和 ARM 架构上&#xff0c;原子操作通常利用硬件提供的原子指令来实现&#xff0c;比如 LOCK 前缀&#xff08;x86&#xff09;或 LDREX/STREX&#xff08;ARM&#xff09;。以下是一些关键的原子操作&#xff08;例如原子递增和比较交换&#xff09;的汇…...

InnoDB事务系统(二):事务的实现

事务隔离性由锁来实现。原子性、一致性、持久性通过数据库的 redo log 和 undo log 来完成。 redo log 称为重做日志&#xff0c;用来保证事务的原子性和持久性。undo log 用来保证事务的一致性。 有的 DBA 或许会认为 undo 是 redo 的逆过程&#xff0c;其实不然。redo 和 u…...

xdoj :模式匹配

模式匹配 题目描述&#xff1a; 接收信号中包含特定的信号模式&#xff0c;对接收信号进行检测&#xff0c;以统计特定模式出现的次数。 例如接收信号为 9 3 5 7 5 8 6 3 5 7 1 9 3 5 7&#xff0c;如果特定信号为 3 5 7&#xff0c;则接收信号中包含了 3 个特定模式。通过键…...

Redis的基本使用命令(GET,SET,KEYS,EXISTS,DEL,EXPIRE,TTL,TYPE)

目录 SET GET KEYS EXISTS DEL EXPIRE TTL redis中的过期策略是怎么实现的&#xff08;面试&#xff09; 上文介绍reids的安装以及基本概念&#xff0c;本章节主要介绍 Redis的基本使用命令的使用 Redis 是一个基于键值对&#xff08;KEY - VALUE&#xff09;存储的…...

LruCache(本地cache)生产环境中遇到的问题及改进

问题&#xff1a;单机qps增加时请求摘要后端&#xff0c;耗时也会增加&#xff0c;因为超过了后端处理能力&#xff08;最大qps&#xff0c;存在任务堆积&#xff09;。 版本一 引入LruCache。为了避免数据失效&#xff0c;cache数据的时效性要小于摘要后端物料的更新时间&…...

智慧公交指挥中枢,数据可视化 BI 驾驶舱

随着智慧城市的蓬勃发展&#xff0c;公共交通作为城市运营的核心枢纽&#xff0c;正朝着智能化和数据驱动的方向演进。通过整合 CAN 总线技术(Controller Area Network&#xff0c;控制器局域网总线)、车载智能终端、大数据分析及处理等尖端技术&#xff0c;构建的公交“大脑”…...

【计算机网络】期末考试预习复习|上

作业讲解 物理层作业 共有4个用户进行CDMA通信。这4个用户的码片序列为&#xff1a; A: (–1 –1 –1 1 1 –1 1 1)&#xff1b;B: (–1 –1 1 –1 1 1 1 –1) C: (–1 1 –1 1 1 1 –1 –1)&#xff1b;D: (–1 1 –1 –1 –1 –1 1 –1) 现收到码片序列&#xff1a;(–1 1 –…...

YOLOv8目标检测(四)_图片推理

YOLOv8目标检测(一)_检测流程梳理&#xff1a;YOLOv8目标检测(一)_检测流程梳理_yolo检测流程-CSDN博客 YOLOv8目标检测(二)_准备数据集&#xff1a;YOLOv8目标检测(二)_准备数据集_yolov8 数据集准备-CSDN博客 YOLOv8目标检测(三)_训练模型&#xff1a;YOLOv8目标检测(三)_训…...

AI工具如何深刻改变我们的工作与生活

在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经从科幻小说中的概念变成了我们日常生活中不可或缺的一部分。从智能家居到自动驾驶汽车&#xff0c;从医疗诊断到金融服务&#xff0c;AI正以惊人的速度重塑着我们的世界。 一、工作方式的革新…...

springboot中——Logback介绍

程序中的日志&#xff0c;是用来记录应用程序的运行信息、状态信息、错误信息等。 Logback基本使用 springboot的依赖自动传递了logback的依赖&#xff0c;所以不用再引入依赖 之后在resources文件下创建logback.xml文件&#xff0c;写入 <?xml version"1.0" …...

【Tomcat】第一站:理解tomcat与Socket

目录 1. Tomcat 1.1 Tomcat帮助启动http服务器。 1.2 tomcat理解&#xff1a; 2. 计算机网络最基本的流程 2.1 信息是怎么来的&#xff1f; 2.2 端口是干什么的&#xff1f; 3. 简单的Socket案例 服务端 客户端 启动&#xff1a; 3.2 在Tomcat发送信息&#xff0c;看…...

TQ15EG开发板教程:使用SSH登录petalinux

本例程在上一章“创建运行petalinux2019.1”基础上进行&#xff0c;本例程将实现使用SSH登录petalinux。 将上一章生成的BOOT.BIN与imag.ub文件放入到SD卡中启动。给开发板插入电源与串口&#xff0c;注意串口插入后会识别出两个串口号&#xff0c;都需要打开&#xff0c;查看串…...

Java从入门到工作4 - MySQL

一&#xff1a;检测数据库网络 telnet 127.0.0.1 3306 注意ip和端口后之间是空格&#xff0c;不需要引号 二&#xff1a;SQL语法 1、创建结果集 SELECT 电视机 AS typeUNION SELECT 电冰箱UNION SELECT 洗衣机UNION SELECT 空调UNION SELECT 电脑UNION SELECT 热水器UNION…...

OpenShift 4 - 多云管理(2) - 配置多集群观察功能

《OpenShift / RHEL / DevSecOps 汇总目录》 本文在 OpenShift 4.17 RHACM 2.12 环境中进行验证。 文章目录 多集群观察技术架构安装多集群观察功能监控多集群的运行状态监控多集群的应用运行在被管集群监控应用运行在管理集群监控被管集群的应用运行 参考 多集群观察技术架构…...

【鸿睿创智开发板试用】RK3568 NPU的人工智能推理测试

目录 引言 驱动移植 例程编译 修改build.sh 执行编译 运行测试 部署libc的库文件 执行测试程序 结语 引言 鸿睿创智的H01开发板是基于RK3568芯片的&#xff0c;瑞芯微芯片的一大特色就是提供了NPU推理的支持。本文将对其NPU推理进行测试。 驱动移植 H01的开发板已经…...

iOS swift开发系列 -- tabbar问题总结

1.单视图如何改为tabbar&#xff0c;以便显示2个标签页 右上角➕&#xff0c;输入tabbar 找到控件&#xff0c;然后选中&#xff0c;把entrypoint移动到tabbar控件 2.改成tabbar&#xff0c;生成两个item&#xff0c;配置各自视图后&#xff0c;启动发现报错 Thread 1: “-[p…...

四、CSS3

一、CSS3简介 1、CSS3概述 CSS3 是 CSS2 的升级版本&#xff0c;他在CSS2的基础上&#xff0c;新增了很多强大的新功能&#xff0c;从而解决一些实际面临的问题。 CSS在未来会按照模块化的方式去发展&#xff1a;https://www.w3.org/Style/CSS/current-work.html …...

Three使用WebGPU的关键TSL

Three.js 使用 WebGPU 的关键 TSL TSL: three.js shader language 介绍 three.js 材质转为webgpu的关键流程, 从而引出 TSL. 1、关键类关系 WebGPURenderer|-- library: StandardNodeLibrary|-- _nodes: Nodes|-- _objects: RenderObjects|-- createRenderObject()StandardN…...

ESP32-S3模组上跑通ES8388(30)

接前一篇文章:ESP32-S3模组上跑通ES8388(29) 二、利用ESP-ADF操作ES8388 2. 详细解析 上一回终于解析完了es8388_init函数的所有代码。本回回到调用它的地方,继续往下讲解。 我们是从ESP32-S3模组上跑通ES8388(7)-CSDN博客开始进入es8388_init函数,展开对于它的解析的…...

概率论得学习和整理24:EXCEL的各种图形,统计图形

目录 0 EXCEL的各种图形&#xff0c;统计图形 1 统计图形 / 直方图 / 其实叫 频度图 hist最合适(用原始数据直接作图) 1.1 什么是频度图 1.2 如何创建频度图&#xff0c;一般是只选中1列数据&#xff08;1个数组&#xff09; 1.3 如何修改频度图的宽度 1.4 hist图的一个特…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...