鸿蒙内核源码分析(原子操作篇) | 谁在为原子操作保驾护航
基本概念
在支持多任务的操作系统中,修改一块内存区域的数据需要“读取-修改-写入”三个步骤。然而同一内存区域的数据可能同时被多个任务访问,如果在修改数据的过程中被其他任务打断,就会造成该操作的执行结果无法预知。
使用开关中断的方法固然可以保证多任务执行结果符合预期,但这种方法显然会影响系统性能。
ARMv6
架构引入了LDREX
和STREX
指令,以支持对共享存储器更缜密的非阻塞同步。由此实现的原子操作能确保对同一数据的“读取-修改-写入”操作在它的执行期间不会被打断,即操作的原子性。
有多个任务对同一个内存数据进行加减或交换操作时,使用原子操作保证结果的可预知性。
看过自旋锁篇的应该对LDREX和STREX指令不陌生的,自旋锁的本质就是对某个变量的原子操作,而且一定要通过汇编代码实现,也就是说LDREX
和STREX
指令保证了原子操作的底层实现.
回顾下自旋锁申请和释放锁的汇编代码.
ArchSpinLock 申请锁代码
FUNCTION(ArchSpinLock) @死守,非要拿到锁mov r1, #1 @r1=11: @循环的作用,因SEV是广播事件.不一定lock->rawLock的值已经改变了ldrex r2, [r0] @r0 = &lock->rawLock, 即 r2 = lock->rawLockcmp r2, #0 @r2和0比较wfene @不相等时,说明资源被占用,CPU核进入睡眠状态strexeq r2, r1, [r0]@此时CPU被重新唤醒,尝试令lock->rawLock=1,成功写入则r2=0cmpeq r2, #0 @再来比较r2是否等于0,如果相等则获取到了锁bne 1b @如果不相等,继续进入循环dmb @用DMB指令来隔离,以保证缓冲中的数据已经落实到RAM中bx lr @此时是一定拿到锁了,跳回调用ArchSpinLock函数
ArchSpinUnlock 释放锁代码
FUNCTION(ArchSpinUnlock) @释放锁mov r1, #0 @r1=0 dmb @数据存储隔离,以保证缓冲中的数据已经落实到RAM中str r1, [r0] @令lock->rawLock = 0dsb @数据同步隔离sev @给各CPU广播事件,唤醒沉睡的CPU们bx lr @跳回调用ArchSpinLock函数
运作机制
鸿蒙通过对ARMv6
架构中的LDREX
和STREX
进行封装,向用户提供了一套原子操作接口。
-
LDREX Rx, [Ry]
读取内存中的值,并标记对该段内存为独占访问:- 读取寄存器Ry指向的4字节内存数据,保存到Rx寄存器中。
- 对Ry指向的内存区域添加独占访问标记。
-
STREX Rf, Rx, [Ry]
检查内存是否有独占访问标记,如果有则更新内存值并清空标记,否则不更新内存:- 有独占访问标记
- 将寄存器Rx中的值更新到寄存器Ry指向的内存。
- 标志寄存器Rf置为0。
- 没有独占访问标记
- 不更新内存。
- 标志寄存器Rf置为1。
- 有独占访问标记
-
判断标志寄存器
标志寄存器为0时,退出循环,原子操作结束。
标志寄存器为1时,继续循环,重新进行原子操作。
功能列表
原子数据包含两种类型Atomic(有符号32位数)与 Atomic64(有符号64位数)。原子操作模块为用户提供下面几种功能,接口详细信息可以查看源码。
此处讲述 LOS_AtomicAdd
, LOS_AtomicSub
,LOS_AtomicRead
,LOS_AtomicSet
理解了函数的汇编代码是理解的原子操作的关键.
LOS_AtomicAdd
//对内存数据做加法
STATIC INLINE INT32 LOS_AtomicAdd(Atomic *v, INT32 addVal)
{INT32 val;UINT32 status;do {__asm__ __volatile__("ldrex %1, [%2]\n""add %1, %1, %3\n" "strex %0, %1, [%2]": "=&r"(status), "=&r"(val): "r"(v), "r"(addVal): "cc");} while (__builtin_expect(status != 0, 0));return val;
}
这是一段C语言内嵌汇编,逐一解读
-
- 先将
status
val
v
addVal
的值交由通用寄存器(R0~R3)接管.
- 先将
-
- %2代表了入参v,[%2]代表的是参数v指向地址的值,也就是 *v ,函数要独占的就是它
-
- %0 ~ %3 对应
status
val
v
addVal
- %0 ~ %3 对应
-
- ldrex %1, [%2] 表示 val = *v ;
-
- add %1, %1, %3 表示 val = val + addVal;
-
- strex %0, %1, [%2] 表示 *v = val;
-
- status 表示是否更新成功,成功了置0,不成功则为 1
-
-
__builtin_expect是结束循环的判断语句,将最有可能执行的分支告诉编译器。
这个指令的写法为:__builtin_expect(EXP, N)。意思是:EXP==N 的概率很大。
综合理解__builtin_expect(status != 0, 0)
说的是status = 0 的可能性很大,不成功就会重新来一遍,直到strex更新成(status == 0)为止.
-
-
- “=&r”(val) 被修饰的操作符作为输出,即将寄存器的值回给val,val为函数的返回值
-
- "cc"向编译器声明以上信息.
LOS_AtomicSub
//对内存数据做减法
STATIC INLINE INT32 LOS_AtomicSub(Atomic *v, INT32 subVal)
{INT32 val;UINT32 status;do {__asm__ __volatile__("ldrex %1, [%2]\n""sub %1, %1, %3\n""strex %0, %1, [%2]": "=&r"(status), "=&r"(val): "r"(v), "r"(subVal): "cc");} while (__builtin_expect(status != 0, 0));return val;
}
解读
- 同
LOS_AtomicAdd
解读
volatile
这里要重点说下volatile
,volatile
提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都要直接从变量地址中读取数据。如果没有volatile
关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
//读取内存数据
STATIC INLINE INT32 LOS_AtomicRead(const Atomic *v)
{return *(volatile INT32 *)v;
}
//写入内存数据
STATIC INLINE VOID LOS_AtomicSet(Atomic *v, INT32 setVal)
{*(volatile INT32 *)v = setVal;
}
编程实例
调用原子操作相关接口,观察结果:
1.创建两个任务
- 任务一用LOS_AtomicAdd对全局变量加100次。
- 任务二用LOS_AtomicSub对全局变量减100次。
2.子任务结束后在主任务中打印全局变量的值。
#include "los_hwi.h"
#include "los_atomic.h"
#include "los_task.h"UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
Atomic g_sum;
Atomic g_count;UINT32 Example_Atomic01(VOID)
{int i = 0;for(i = 0; i < 100; ++i) {LOS_AtomicAdd(&g_sum,1);}LOS_AtomicAdd(&g_count,1);return LOS_OK;
}UINT32 Example_Atomic02(VOID)
{int i = 0;for(i = 0; i < 100; ++i) {LOS_AtomicSub(&g_sum,1);}LOS_AtomicAdd(&g_count,1);return LOS_OK;
}UINT32 Example_TaskEntry(VOID)
{TSK_INIT_PARAM_S stTask1={0};stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Atomic01;stTask1.pcName = "TestAtomicTsk1";stTask1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;stTask1.usTaskPrio = 4;stTask1.uwResved = LOS_TASK_STATUS_DETACHED;TSK_INIT_PARAM_S stTask2={0};stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Atomic02;stTask2.pcName = "TestAtomicTsk2";stTask2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;stTask2.usTaskPrio = 4;stTask2.uwResved = LOS_TASK_STATUS_DETACHED;LOS_TaskLock();LOS_TaskCreate(&g_testTaskId01, &stTask1);LOS_TaskCreate(&g_testTaskId02, &stTask2);LOS_TaskUnlock();while(LOS_AtomicRead(&g_count) != 2);dprintf("g_sum = %d\n", g_sum);return LOS_OK;
}
结果验证
g_sum = 0
经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?
为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案)
OpenHarmony 开发环境搭建
《OpenHarmony源码解析》
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……
OpenHarmony 设备开发学习手册
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请移步前往
相关文章:

鸿蒙内核源码分析(原子操作篇) | 谁在为原子操作保驾护航
基本概念 在支持多任务的操作系统中,修改一块内存区域的数据需要“读取-修改-写入”三个步骤。然而同一内存区域的数据可能同时被多个任务访问,如果在修改数据的过程中被其他任务打断,就会造成该操作的执行结果无法预知。 使用开关中断的方…...

vue3+ts封装axios以及解决跨域问题
目录 一、前言二、封装axios三、 解决跨域四、调用接口五、运行结果 一、前言 前端请求后端数据时,会用到axios,但是如果不将axios封装好,会导致代码冗余 二次封装的好处如下: 求头能统一处理便于接口的统一管理解决回调地狱配置…...

各厂家BI对比
帆软BI、奥威BI、永洪BI、思迈特BI、亿信华辰BI是国内知名的BI产品,不少企业在选型BI软件时都需要对这些BI软件进行了解,从中选择适合自己的一款。经过过年的发展,这些BI(商业智能)软件各自在多个行业中都有广泛的应用…...
SQL - 触发器
触发器是在插入、更新和删除语句前后自动执行的一堆SQL代码,但是触发器被触发后只会执行一次,通常我们使用触发器增强数据的一致性。创建触发器 -- 创建触发器 drop trigger if exists payments_after_insert; delimiter $$ -- 在 payments表 insert 之后…...

Redis中缓存穿透、缓存击穿、缓存雪崩的详解
如何理解Redis缓存的穿透、击穿、雪崩问题: 缓存穿透 是指缓存中和数据库中都没有数据,而用户不断访问,导致这个不存在的数据每次请求都要到存储层去查询,这样失去了意义。 缓存穿透的解决方案有哪些? 缓存null值布隆过滤增强…...

[Meachines] [Medium] Popcorn SQLI+Upload File+PAM权限提升
信息收集 IP AddressOpening Ports10.10.10.6TCP:22,80 $ nmap -p- 10.10.10.6 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.1p1 Debian 6ubuntu2 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: …...
【Linux】python进程管理之supervisor安装使用教程
安装supervisor pip install supervisor生成配置文件 echo_supervisord_conf > /etc/supervisord.conf修改配置文件 vim /etc/supervisord.conf[unix_http_server] file/run/supervisor.sock ; the path to the socket file[supervisord] logfile/var/log/supervisord.log…...

BEM架构
视频 总结: BEM架构:一个命名类的规范而已,说白了就是如何给类起名字使用sass的目的:在<style>中模块化的使用类名,同时减少代码数量 1、 BEM架构 (通义灵码查询结果) BEM (Block Ele…...

物联网(IoT)详解
物联网(IoT)详解 1. IoT定义简介2. IoT工作原理3. IoT关键技术4. 物联网与互联网区别5. IoT使用场景6. 开源物联网平台7. 参考资料 1. IoT定义简介 首先第一个问题,什么是物联网(IoT)? 物联网(英文&#…...
ansync/await 运行流程图
1、流程图: 2、await 之后的方法是何时执行,如何执行的? await 的方法在 Task 执行完成之后,通过调用 Finish 方法执行的。 具体的执行步骤是先将 MoveNext 方法注册到 Task 的回调里,然后在 Task 执行完后调用这个方法…...
生产环境docker nginx+php8.0镜像
生产环境docker nginxphp8.0镜像 自定义创建php8.0镜像,创建dockerfile FROM php:8.0-fpm# 安装系统依赖 RUN sed -i s|http://deb.debian.org/debian|http://mirrors.aliyun.com/debian|g /etc/apt/sources.list && \apt-get update && apt-get i…...

【Hadoop】核心组件深度剖析:HDFS、YARN与MapReduce的奥秘
🐇明明跟你说过:个人主页 🏅个人专栏:《大数据前沿:技术与应用并进》🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、Hadoop简介 2、Hadoop生态系统概览 二、Hadoo…...
Docker Swarm部署SpringCloud Alibaba微服务踩坑记录
为了方便部署和维护微服务项目,还是得上集群部署方案,决定采用Docker的swarm,为什么不是k8s,因为部署骑来又是个新的工具,之前就一直用的docker,自带了类k8s的工具,索性就直接使用swarm了&#…...
深入理解Spring Boot中的AOP应用:从基础组件到高级功能的实现
深入理解Spring Boot中的AOP应用:从基础组件到高级功能的实现 在现代Java开发中,Spring Boot因其简洁性和强大的功能而被广泛采用。而AOP(面向切面编程)作为Spring框架的核心特性之一,为开发者提供了在不修改业务代码的…...
《区块链与监管合规:在创新与规范之间寻求平衡》
区块链技术作为近年来最具创新性和颠覆性的技术之一,已经在金融、供应链、医疗、物联网等多个领域展现出巨大的潜力。然而,随着其应用的不断拓展,如何应对监管和合规性要求成为了区块链发展道路上一个至关重要的问题。 区块链的去中心化、匿…...
Nuxt3【服务器】server 详解
server 文件夹中的内容,会被自动注册为API和服务器处理程序。 服务器 API 对应路径 server/api server/api/hello.ts export default defineEventHandler((event) > {return {hello: world} })页面中使用 <script setup lang"ts"> const { da…...
防火墙技术原理与应用
防火墙概述 防火墙概念 防火墙:通过一种网络安全设备,控制安全区域间的通信,隔离有害通信,进而阻断网络攻击。一般安装在不同安全区域边界处,用于网络通信安全控制,由专用硬件或软件系统组成。 根据网络安全信任程度和需保护的对象,划分安全区域 公共外部网络:Inter…...

【BUU】[NewStarCTF 2023 公开赛道]Final -CP读取文件内容
漏洞检测 访问首页发现是ThinkPHP5 的站点 用工具扫描一下,发现存在ThinkPHP5.0.23 RCE漏洞 访问验证,写入shell 成功写入shell. 根目录发现flag,但是权限不足 提权获取flag 准备提权,这里一开始尝试了find,但是find权限不足 尝试采用cp命令,移动到web目录,发现访问还是…...
火绒安全:一款强大且高效的国产杀毒软件
火绒安全(Huorong Security)是一款国产的杀毒软件,以其轻量、高效和对系统资源占用低的特点受到广泛欢迎。与许多其他杀毒软件不同,火绒注重低调实用,旨在为用户提供无干扰且稳定的系统保护。 火绒安全的主要特点&…...
Oracle 的DBA有哪些权限
Oracle数据库的**DBA(数据库管理员)**拥有全部特权,是Oracle数据库系统最高权限的用户。DBA的权限包括但不限于: 1.创建和管理数据库结构: DBA可以创建、修改和删除数据库中的所有对象,如表、索引、视图等&…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...