ARM64 linux并发与同步之原子操作

卷2:调试与案例分析

第一章 并发与同步

画了两张简图,方便理解,如下:
在这里插入图片描述
在这里插入图片描述
针对并发源的问题,我接触的项目中都是SMP系统,目前大部分也都是SMP系统;
对于SMP系统,情况会更复杂。
□ 同一类型的中断处理程序不会并发执行,但是不同类型的中断可能送达不同的CPU, 因此不同类型的中断处理程序可能会并发执行。
□ 同一类型的软中断会在不同的CPU上并发执行。
□ 同一类型的tasklet是串行执行的,不会在多个CPU上并发执行。
□ 不同CPU上的进程上下文会并发执行。

一般实际项目开发中,需要考虑的问题,书中有写到几种场景,也是比较容易忽略的:

  1. 进程呈下文在操作某个临界区中的资源时发生了中断, 恰巧在对应中断处理程序中也访问
    了这个资源。如果不使用内核同步机制来保护,那么可能会发生并发访问的bug。
  2. 如果进程上下文正在访问和修改临界区中的资源时发生了抢占调度,可能会发生并发访问的bug。
  3. 如果在自旋锁的临界区中主动睡眠以让出CPU,那这也可能是一个并发访问的bug。
  4. 如果两个CPU同时修改临界区中的一个资源,那这也可能是一个bug。
  5. 对临界区数据来说需要考虑从中断处理程序、工作线程(worker)处理程序、tasklet处理程序、软中断处理程序等有没有可能并发访问?
  6. 若从当前内核代码路径访问该数据时发生被抢占,被调度、执行的进程会不会访问该数据?
  7. 进程会不会进入睡眠状态以等待该数据?

1.1 原子操作

原子操作是指“原子地"(不间断地)完成’'读-修改-回写”机制,中间不能被打断,保证数据的有效修改;
举个例子:

static int i =0;
//线程A函数void thread A func()
{i++;
}
//线程B函数void thread B func()
{i++;
}

存在数据段中的变量i的结果是多少?有人说是2,有人说不是2;
代码执行过程如下:
在这里插入图片描述
从上面的代码执行过程来看,最终结果可能等于1。因为变量i是临界区的一个,CPU0和CPU1
可能同时访问,发生并发访问。

有的读者认为可以使用加锁的方式,如使用自旋锁来保证i++操作的原子性,但是加锁操作会导致比较大的开销,用在这里有些浪费。杀鸡焉用牛刀!

Linux内核提供了 atomic类型的原子变量,atomic_t类型的具体定义为如下。

<include/linux/types.h>typedef struct ( int counter; } atomic_t;
1. 基本原子操作函数

Linux内核提供最基本的原子操作函数包括atomic_read()函数和atomic_set()函数。

<include/asm-generic/atomic.h>#define ATOMIC_INIT (i) //原子变量初始化为 i
#define atomic_read (v)  //读取原子变量的值
#define atomic_set (v, i)  //设置变量 v 的值为 i

上述两个函数直接调用READ_ONCE()宏或者WRITE_ONCE()宏来实现,不包括“读-修改-回写”机制,直接使用上述函数容易引发并发访问。

2. 不带返回值的原子操作函数
atomic_inc(v):原子地给v 加 1 
atomic_dec(v):原子地给 v 减 1 
atomic_add(i,v):原子地给 v 加 i
atomic_and(i,v):原子地给v和i做“与”操作。 
atomic_or(i,v):原子地给v和i做"或”操作。 
atomic_xor(i,v):原子地给v和i做“异或”操作。

在这里我不对所有的api接口展开说,有兴趣可以看下下面路径头文件中的API, 讲述了所有关于原子操作的API接口;

kernel/linux/linux-5.15.73/include/linux/atomic/atomic-instrumented.h

接下来我们试图探一下原理:
我们以atomic_add为例

//kernel/linux/linux-5.15.73/include/linux/atomic/atomic-instrumented.h 
atomic_add(int i, atomic_t *v)
{instrument_atomic_read_write(v, sizeof(*v));arch_atomic_add(i, v);
}
kernel/linux/linux-5.15.73/include/asm-generic/atomic.h
#define arch_atomic_add_return			generic_atomic_add_return
#define arch_atomic_sub_return			generic_atomic_sub_return#define arch_atomic_fetch_add			generic_atomic_fetch_add
#define arch_atomic_fetch_sub			generic_atomic_fetch_sub
#define arch_atomic_fetch_and			generic_atomic_fetch_and
#define arch_atomic_fetch_or			generic_atomic_fetch_or
#define arch_atomic_fetch_xor			generic_atomic_fetch_xor#define arch_atomic_add				generic_atomic_add
#define arch_atomic_sub				generic_atomic_sub
#define arch_atomic_and				generic_atomic_and
#define arch_atomic_or				generic_atomic_or
#define arch_atomic_xor				generic_atomic_xor
kernel/linux/linux-5.15.73/include/asm-generic/atomic.h 
#ifdef  CONFIG_SMP //多核
#define ATOMIC_OP(op, c_op)						\
static inline void generic_atomic_##op(int i, atomic_t *v)		\
{									\int c, old;							\\c = v->counter;							\while ((old = arch_cmpxchg(&v->counter, c, c c_op i)) != c)	\c = old;						\
}
#else
#define ATOMIC_OP(op, c_op)						\
static inline void generic_atomic_##op(int i, atomic_t *v)		\
{									\unsigned long flags;						\\raw_local_irq_save(flags);					\v->counter = v->counter c_op i;					\raw_local_irq_restore(flags);					\
}
#endif

看到如果是多核,最终会调到arch_cmpxchg函数,如果是单核,就调用raw_local_irq_save,保存并禁用本地中断,防止抢占;
继续分析多核调用;

//kernel/linux/linux-5.15.73/arch/arm64/include/asm/cmpxchg.h #define arch_cmpxchg(...)		__cmpxchg_wrapper( _mb, __VA_ARGS__) //展开__cmpxchg_wrapper( _mb, &v->counter, c, c c_op i)
#define __cmpxchg_wrapper(sfx, ptr, o, n)				\
({									\__typeof__(*(ptr)) __ret;					\__ret = (__typeof__(*(ptr)))					\__cmpxchg##sfx((ptr), (unsigned long)(o),		\(unsigned long)(n), sizeof(*(ptr)));	\__ret;								\
})
// 展开__cmpxchg_mb(&v->counter, c, c c_op i, sizeof(&v->counter)), 64位系统sizeof(&v->counter)=8#define __CMPXCHG_GEN(sfx)						\
static __always_inline unsigned long __cmpxchg##sfx(volatile void *ptr,	\unsigned long old,		\unsigned long new,		\int size)			\
{									\switch (size) {							\case 1:								\return __cmpxchg_case##sfx##_8(ptr, old, new);		\case 2:								\return __cmpxchg_case##sfx##_16(ptr, old, new);		\case 4:								\return __cmpxchg_case##sfx##_32(ptr, old, new);		\case 8:								\return __cmpxchg_case##sfx##_64(ptr, old, new);		\default:							\BUILD_BUG();						\}								\\unreachable();							\
}//展开__cmpxchg_case_mb_64(&v->counter, c, c c_op i)#define __CMPXCHG_CASE(name, sz)			\
static inline u##sz __cmpxchg_case_##name##sz(volatile void *ptr,	\u##sz old,		\u##sz new)		\
{									\return __lse_ll_sc_body(_cmpxchg_case_##name##sz,		\ptr, old, new);				\
}//展开 __lse_ll_sc_body(__cmpxchg_case_mb_64, &v->counter, c, c c_op i)

根据上述逐级展开,可以追寻到下面定义,坚持下快找到真相了。

kernel/linux/linux-5.15.73/arch/arm64/include/asm/lse.h
#ifdef CONFIG_ARM64_LSE_ATOMICS...#define __lse_ll_sc_body(op, ...)					\
({									\system_uses_lse_atomics() ?					\__lse_##op(__VA_ARGS__) :				\__ll_sc_##op(__VA_ARGS__);				\
})...#else	/* CONFIG_ARM64_LSE_ATOMICS */#define __lse_ll_sc_body(op, ...)		__ll_sc_##op(__VA_ARGS__)...
#endif	/* CONFIG_ARM64_LSE_ATOMICS */

可以看到如果定义CONFIG_ARM64_LSE_ATOMICS这个宏,走LSE(是否支持大系统扩展),若定义了这个宏走下面LSE流程实现

kernel/linux/linux-5.15.73/arch/arm64/include/asm/atomic_lse.h
#define __CMPXCHG_CASE(w, sfx, name, sz, mb, cl...)			\
static __always_inline u##sz						\
__lse__cmpxchg_case_##name##sz(volatile void *ptr,			\u##sz old,		\u##sz new)		\
{									\register unsigned long x0 asm ("x0") = (unsigned long)ptr;	\register u##sz x1 asm ("x1") = old;				\register u##sz x2 asm ("x2") = new;				\unsigned long tmp;						\\asm volatile(							\__LSE_PREAMBLE							\"	mov	%" #w "[tmp], %" #w "[old]\n"			\"	cas" #mb #sfx "\t%" #w "[tmp], %" #w "[new], %[v]\n"	\"	mov	%" #w "[ret], %" #w "[tmp]"			\: [ret] "+r" (x0), [v] "+Q" (*(unsigned long *)ptr),		\[tmp] "=&r" (tmp)						\: [old] "r" (x1), [new] "r" (x2)				\: cl);								\\return x0;							\
}

w:表示位宽,支持8位、16位、32位以及64位。
sfx: cas指令的位宽后缀,8位宽使用b后缀,16位宽使用h后缀。
name: 表示内存屏障类型,如“acq_”表示支持加载-获取内存屏障原语,“rel_”表示支持存储-释放内存屏障原语,“mb_”表示同时支持加载-获取和存储-释放内存屏障原语。
sz:位宽大小。
mb:组成cas指令的内存屏障后缀,"a"表示加载-获取内存屏障原语,“l” 表示存储释放内存屏障原语,“al"表示同时支持加载-获取和存储-释放内存屏障原语。
c1:内嵌汇编的损坏部。

从上述内嵌汇编可以看到这段内嵌汇编实现了一个包含原子比较并交换(CAS)指令的操作(比较晦涩难懂),看个大概吧;

若没有定义这个宏走下面流程实现

//kernel/linux/linux-5.15.73/arch/arm64/include/asm/atomic_ll_sc.h #define __CMPXCHG_CASE(w, sfx, name, sz, mb, acq, rel, cl, constraint)	\
static inline u##sz							\
__ll_sc__cmpxchg_case_##name##sz(volatile void *ptr,			\unsigned long old,		\u##sz new)			\
{									\unsigned long tmp;						\u##sz oldval;							\\/*								\* Sub-word sizes require explicit casting so that the compare  \* part of the cmpxchg doesn't end up interpreting non-zero	\* upper bits of the register containing "old".			\*/								\if (sz < 32)							\old = (u##sz)old;					\\asm volatile(							\__LL_SC_FALLBACK(						\"	prfm	pstl1strm, %[v]\n"				\"1:	ld" #acq "xr" #sfx "\t%" #w "[oldval], %[v]\n"		\"	eor	%" #w "[tmp], %" #w "[oldval], %" #w "[old]\n"	\"	cbnz	%" #w "[tmp], 2f\n"				\"	st" #rel "xr" #sfx "\t%w[tmp], %" #w "[new], %[v]\n"	\"	cbnz	%w[tmp], 1b\n"					\"	" #mb "\n"						\"2:")								\: [tmp] "=&r" (tmp), [oldval] "=&r" (oldval),			\[v] "+Q" (*(u##sz *)ptr)					\: [old] __stringify(constraint) "r" (old), [new] "r" (new)	\: cl);								\\return oldval;							\
}

从上述内嵌汇编中可以看到使用Idxr和stxr 独占访问指令的组合;

====================================================================================================
接下来我们简单学习下CAS指令和LDXR/STXR独占访问指令

CAS指令:
ARM64处理器提供了比较并交换指令— cas指令。cas指令根据不同的内存屏障属性分成4类:
□ 隐含了加载-获取内存屏障原语。
□ 隐含了存储-释放内存屏障原语。
□ 同时隐含了加载-获取和存储-释放内存屏障原语。
□ 不隐含内存屏障原语。
在这里插入图片描述
在这里插入图片描述

LDXR/STXR独占访问指令:
ARMv8体系结构都提供了独占访问的的指令,在A64指令集中,LDXR指令尝试在内存总线中申请一个独占访问的锁,然后访问一个内存地址。STXR指令会往刚才LDXR指令已经申请独占访问的内存地址中写一个新内容,通常组合使用LDXR和STXR指令来完成一些同步操作;
它们的操作格式如下:

LDXR 指令:

LDXR <Wt>, [<Xn|SP>{, #<imm>}]<Wt>:目标寄存器,用于存储从内存中加载的值。
[<Xn|SP>{, #<imm>}]:源内存地址,可以通过基址寄存器 <Xn> 或栈指针寄存器 SP 加上可选的偏移量 <imm> 来指定。

STXR 指令:

STXR <Ws>, <Wt>, [<Xn|SP>{, #<imm>}]<Ws>:用于指示是否成功执行存储操作的结果寄存器。
<Wt>:要存储到内存中的值的寄存器。
[<Xn|SP>{, #<imm>}]:目标内存地址,可以通过基址寄存器 <Xn> 或栈指针寄存器 SP 加上可选的偏移量 <imm> 来指定。

在这两个指令中,Xn 和 SP 分别代表基址寄存器和栈指针寄存器,imm 为可选的偏移量。这些指令允许对内存进行原子操作,并且通常与并发控制相关的算法一起使用,以确保对共享内存的原子性访问。

还有多字节(16字节)独占访问的扩展指令LDXP和STXP,有兴趣可以自行学习;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/183228.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据可视化:动态柱状图

终于来到最后一个数据可视化的文章拿啦~~~ 在这里学习如何绘制动态柱状图 我先整个活 (๑′ᴗ‵๑)&#xff29; Lᵒᵛᵉᵧₒᵤ❤ 什么是pyecharts&#xff1f; 答&#xff1a; Python的Pyecharts软件包。它是一个用于Python数据可视化和图表绘制的库&#xff0c;可用于制作…

音乐免费下载mp3格式+音频格式转换+剪辑音频+合并音频教程

1.在qq音乐网页版搜索想要的歌曲 qq音乐网站&#xff1a;https://y.qq.com/ 如果你是vip可以直接下载vip的歌曲&#xff0c;如果不是选择不是vip的歌曲进行第一步的操作 2.点击播放进入页面后F12拿到音频地址 然后双击src里面的音频地址复制 网页新标签打开赋值的这个链接&a…

SpringBoot-WebSocket浏览器-服务器双向通信

文章目录 WebSocket 介绍入门案例 WebSocket 介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输。 应用场景&#xff1a; 视…

Markdown语法教程

Markdown&#xff1a;一种轻量级语言&#xff0c;有简洁的编写方式&#xff0c;能够提高大家的工作效率。 一、标题 1.1 标题 标题的编写格式以#号开始&#xff0c;分别表示h1 ~ h6&#xff0c;注意&#xff1a;# 后面有空格&#xff01; # 一级标题 ## 二级标题 ### 三级标题…

vue2.0 打包,nginx部署

1、修改这里为空 否则报错&#xff1a;vue is undefined 2、修改为hash&#xff0c;重点&#xff1a;打包dist文件运行&#xff0c;必须这样 3、安装ngnix&#xff0c;重点&#xff1a;使用node的包&#xff1a;httpserve&#xff0c;失败 4、重点&#xff1a;配置代理转发 前端…

2024天津理工大学中环信息学院专升本机械设计制造自动化专业考纲

2024年天津理工大学中环信息学院高职升本科《机械设计制造及其自动化》专业课考试大纲《机械设计》《机械制图》 《机械设计》考试大纲 教 材&#xff1a;《机械设计》&#xff08;第十版&#xff09;&#xff0c;高等教育出版社&#xff0c;濮良贵、陈国定、吴立言主编&#…

ElementUI-tree拖拽功能与节点自定义

前言 在管理端会遇到多分类时&#xff0c;要求有层次展示出来&#xff0c;并且每个分类有额外的操作。例如&#xff1a;添加分类、编辑分类、删除、拖到分类等。 下面将会记录这样的一个需求实习过程。 了解需求 分类展示按层级展示分类根据特定的参数展示可以操作的按钮&a…

四阶龙格库塔与元胞自动机

龙格库塔法参考&#xff1a; 【精选】四阶龙格库塔算法及matlab代码_四阶龙格库塔法matlab_漫道长歌行的博客-CSDN博客 龙格库塔算法 Runge Kutta Method及其Matlab代码_龙格库塔法matlab_Lzh_023016的博客-CSDN博客 元胞自动机参考&#xff1a; 元胞自动机&#xff1a;森林…

Leetcode Daily Challenge 1845. Seat Reservation Manager

1845. Seat Reservation Manager 题目要求&#xff1a;初始化一个SeatManager类包括默认构造函数和类函数&#xff0c;所有的seat初始化为true。reverse函数返回最小的true&#xff0c;然后把这个编号的椅子赋值为false。unreverse(seatNumber)函数把编号为seatNumber的椅子恢…

阿里云中的云服务器的ubuntu中的vim没有显示行号

没有行号&#xff1a; 在终端输入命令&#xff1a; vim ~/.vimrc set nu

【PC电脑windows环境下-[jetson-orin-NX]Linux环境下-下载工具esptool工具使用-相关细节-简单样例-实际操作】

【PC电脑windows环境下-[jetson-orin-NX]Linux环境下-下载工具esptool工具使用-相关细节-简单样例-实际操作】 1、概述2、实验环境3、 物品说明4-2、自我总结5、本次实验说明1、准备样例2、设置芯片3、编译4、下载5、验证 &#xff08;1&#xff09;windows环境下进行烧写1、下…

java泛型的深入 泛型还可以在很多地方进行定义 泛型类 泛型方法 泛型接口 泛型的继承和通配符 泛型类练习

文章目录 泛型的深入泛型还可以在很多地方进行定义泛型类泛型方法泛型接口 泛型的继承和通配符泛型类练习总结 泛型的深入 public static void main(String[] args) {//在没有泛型的时候怎么存储数据ArrayList listnew ArrayList();list.add(1);list.add("abc");//遍…

上海:竹云董事长董宁受邀在第三届“双区驱动,打造全球经济新引擎”国际合作论坛发言

作为中国最具活力的两大重要经济带&#xff0c;粤港澳和长三角两大湾区2022年GDP总量超过42万亿&#xff0c;占全国GDP总量的35%&#xff0c;对中国经济的重要性举足轻重。中国国际进口博览会是我国主动向世界开放市场的重大举措&#xff0c;是一个推动两地开放合作&#xff0c…

Ubuntu18.04安装pcl-1.12.1,make时报错:/usr/bin/ld: cannot find -lvtkIOMPIImage

解决方案&#xff1a; 在vtk安装包中&#xff0c;重新打开cmake-gui&#xff0c;然后勾选上VTK_Group_MPI和VTK_Group_Imaging。 cd VTK-8.2.0 cd build cmake-gui然后重新编译生成。 make -j8 # 或者j4,量力而行。 sudo make install 就可以解决了。 然后重新回到pcl安装…

V-REP和Python的联合仿真

机器人仿真软件 各类免费的的机器人仿真软件优缺点汇总_robot 仿真 软件收费么_dyannacon的博客-CSDN博客 课程地址 https://class.guyuehome.com/p/t_pc/course_pc_detail/column/p_605af87be4b007b4183a42e7 课程资料 guyueclass: 古月学院课程代码 旋转变换 旋转的左乘与…

论文阅读—— UniDetector(cvpr2023)

arxiv&#xff1a;https://arxiv.org/abs/2303.11749 github&#xff1a;https://github.com/zhenyuw16/UniDetector 一、介绍 通用目标检测旨在检测场景那种的一切目标。现有的检测器依赖于大量数据集 通用的目标检测器应该有两个能力&#xff1a;1、可以利用多种来…

数据库实验:SQL的数据视图

目录 视图概述视图的概念视图的作用 实验目的实验内容实验要求实验过程 视图概述 视图是由数据库中的一个表或多个表导出的虚拟表&#xff0c;其作用是方便用户对数据的操作 视图的概念 视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一…

SQL审计是什么意思?目的是什么?有什么好处?

很多刚入行的运维小伙伴对于SQL审计不是很了解&#xff0c;不知道其是什么意思&#xff1f;使用SQL审计的目的是什么&#xff1f;使用SQL审计的好处有哪些&#xff1f;这里我们大家就来一起聊聊&#xff0c;仅供参考哈&#xff01; SQL审计是什么意思&#xff1f; 【回答】&…

CoCa论文笔记

摘要 计算机视觉任务中&#xff0c;探索大规模预训练基础模型具有重要意义&#xff0c;因为这些模型可以可以极快地迁移到下游任务中。本文提出的CoCa&#xff08;Contrastive Captioner&#xff09;&#xff0c;一个极简设计&#xff0c;结合对比损失和captioning损失预训练一…

24PL-18-50-1836、12PN-4.1-50-1836比例电磁铁控制器

12PG-2.2-40-1836、24PG-8.8-40-1836、12PT-40-1836、24PT-40-1836、12PL-2.2-40-1836、24PL-8.8-40-1836、12PN-2.2-40-1836、24PN-8.8-40-1836、12PG-4.1-50-1836、24PG-18-50-1836、12PT-4.1-50-1836、24PT-18-50-1836、12PL-4.1-50-1836、24PL-18-50-1836、12PN-4.1-50-183…