volatile 关键字的使用

news2024/11/17 13:52:36

写博客的目的第一是做笔记,第二是在错误的基础上不断刷新认知,这两天会写三篇关于嵌入式容易混淆的知识点,有错误欢迎拍砖!

1、volatile关键字的使用

关于volatile 关键字,如果你的理解仅仅是讲“是从内存直接取数据”,那实际上你对他理解还差一些火候。
实际上使用volatile 关键字声明变量的时候,编译器对访问的变量的代码不再进行优化。注意,这个关键字针对的对象是编译器,告诉编译器对该变量访问不进行优化。而导致的结果是我们看到的对该变量访问会直接访问内存,从而提供对变量的稳定访问。
未使用volatile修饰的变量,编译器可能优化读取和存储。可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,就会出现不一致的现象。注意,我这里说的是“寄存器”,没有强调是不是缓存(编译器翻译成汇编指令可以操作指定的寄存器进行优化,但是并不能操作缓存进行优化,对于内存地址来说缓存自身进行支配),下面会说到为什么和缓存没有关系。先说下编译器优化。

1.1 优化

为了提高程序的运行效率,会进行包括硬件和软件在内的优化。
硬件级别的优化:处理引入cache机制外,现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令硬件可以乱序执行,以充分利用利用CPU的指令流水线,提高执行速度。
软件级别的优化:软件的优化一种是写代码时由程序员优化,另一种是由编译器进行的优化。编译器优化的常用方法有:将内存变量缓存到寄存器;调整指令顺序以充分利用CPU指令流水线,常见的是重新排序读写指令。
(由编译器优化或者硬件进行的重新排序引起的问题解决思路是必须在必须以特定顺序执行的操作之间设置内存屏障(memory barrier),其中linux提供了一个宏来解决编译器的执行顺序问题,但是硬件顺序没法解决。void Barrier(void)这个函数用来通知编译器插入一个内存屏障,对硬件无效,编译后的代码会把当前CPU中所有修改过的数据存入内存,需要这些数据的时序再重新从内存中读出)。
特别需要记住,C 编译器是没有线程概念的,感受不到异步事件对该变量的操作。也就是同样一个变量在多线程(中断也可以理解为一个线程),编译优化后的程序一个线程或任务一个函数操作的是寄存器该变量的副本,另一个线程或者中断操作的一个函数可能是变量的内存或cache

1.2 使用Volatile的几种场景

首先要说明的是,这些场景不使用volatile限定就一定会出错吗,不一定。首先,volatile 是告诉编译器防优化的,和编译器的优化等级有关(优化等级高,可能出错的概率高);其次,在编译器优化时,由于寄存器数量有限,未加限定的变量不一定被优化到了寄存器(通过寄存器操作的数据“备份”)。通常的情况就是没有volatile可以正常运行,修改了编译器的优化级别之后就又不能正常运行了。debug版本正常,但是release版本却不能正常的问题。

1)存储器映射的硬件寄存器通常要加volatile说明,因为每次对存储器映射的硬件寄存器读或写都可能有不同意义。

对于读操作,无论是读的可能是编译器优化取出的寄存器的“副本”或者读的cache,都存在实际外部输入是直接改变的内存,而寄存器或者cache是未感知的。比如说我们从指定的内存映射寄存器地址读取串口接收FIFO。
对应写操作,我们可能就是要通过寄存器实现对外部硬件控制。如:
0x12345678=0x55;
0x12345678=0x56;
0x12345678=0x57;
0x12345678=0x58;
表示对外部接口执行4个不同动作,而编译器优化会认为只执行第4条语句。

在1)这种场景下,除了编译器优化外,由于有cache的存在,也可能导致读写操作的是cache而使得实际读写未感知外部变化或者输出问题。我们可以理解为虽然volatile只是告诉编译器直接操作内存,但是实际上也把cache的问题跳过解决掉了。

2)中断服务函数中修改共其他程序监测的变量需要加volatile限定:

比如一个函数读操作一个变量A,中断处理函数写操作变量A。对于编译器由于没有线程概念(中断处理函数可以理解为特殊的线程),不会考虑到异步事件,编译器判断一个函数中没有修改过A,因此只执行一次A到寄存器的读操作,然后每次都只使用A在寄存器的副本。而中断处理函数中修改了A的值(实际内存或cache),该函数读操作寄存器“副本”就会得到错误结果。
比如下面是一个O2级别的优化:
在这里插入图片描述
这段汇编代码是:
wait:
mov eax, DWORD PTR busy[rip]
.L2:
test eax, eax
jne .L2
ret
busy:
.long 1

其中L2这一段即为while循环,这段指令是经过编译器优化的,可以看到,决定能否跳出循环是通过检查寄存器eax来完成的,而没有检查变量busy所在内存的真实内容。
加入volatile限定busy后的编译器生成汇编代码如下:
wait:
.L2:
mov eax, DWORD PTR busy[rip]
test eax, eax
jne .L2
ret
busy:
.long 1
注意看此时L2这一段,每次都从busy变量所在的内存中读取数据并存放在eax,然后再去判断,这样就能确保每次都能读取到busy变量的最新值。

3)多任务环境下各任务间共享的标志应该加volatile:

该情况和2)的原理实际上是一样的,就是编译器是不知道有异步事件改变变量的。2)和3)的情况都可能是由于一个任务操作的是在寄存器中的副本,一个操作的是实际内存或cache导致的不一致问题。
在这里插入图片描述
这个说个题外话,2)和3)不一致的原因是由于编译器优化的是寄存器。

思考:假如说编译器不优化(即没有寄存器副本引入的问题),在开启cache的情况下,线程间A和线程B都操作不加volatile限定,会有什么问题吗?答案是不会,因为即使在cache中,线程A或者线程B在使用变量a是发现cache有该变量,都会直接从cache中拿来使用,a变量对于A线程和B线程并不会有什么不一致问题,专门这么说只是用来告诉大家,volatile引入的不一致与cache没有半毛钱关系,只是与编译器优化的执行顺序(如1)中写操作)或寄存器副本( 1)读2)3) )相关。

1.3 Volatile 和原子化操作有关系吗

先说答案,Volatile和原子操作没有半毛钱关系,意思就是原本是原子操作加上它还是原子的,原本不是原子操作加上它也仍然是非原子的。对应本身具有原子性单个变量的读/写加上它也是原子的,对于类似于i++这种复合操作加上它限定也仍然不具有原子性。所以原子话得操作还需要进行加锁处理。
关于内容,明天补充。。。

2、关于使用队列解决同步问题的解释

3、关于elf解析文件、map文件的说明

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CoreDNS

目录 文章目录目录本节实战前言1、环境变量2、DNS1.DNS 解析过程2.根域名服务器3.顶级域名服务器4.权威性域名服务器5.dig 域名3、CoreDNS1.CoreDNS 扩展配置(1)开开启日志服务(2)特定域名使用自定义 DNS 服务器(3&…

K8s集群部署(二进制安装部署详细手册)

一、简介 K8s部署主要有两种方式:1、Kubeadm Kubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。 2、二进制 从github下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群…

SSM项目搭建保姆级教程

文章目录1、什么是SSM框架1.1、持久层1.2、业务层1.3、表现层1.4、View层1.5、SpringMVC执行流程1.6、MyBatis2、SSM实战搭建2.1、创建工程2.2、添加依赖2.3、配置spring.xml文件2.4、配置web.xml文件2.5、log4j.properties2.6、准备表2.7、实体类2.8、mapper2.9、service2.10、…

GuLi商城-SpringCloud-OpenFeign测试远程调用

1. Feign 简介 Feign 是一个声明式的 HTTP 客户端,它的目的就是让远程调用更加简单。Feign 提供了HTTP请 求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信 息。Feign 整合了 Ribbon(负载…

操作系统 二(进程管理)

一、进程的定义及特征进程的定义由程序、数据、进程控制块三部分组成为了使程序可以并发执行,且可以对并发执行的程序加以描述和控制。不同角度的定义:进程是程序的一次执行;进程是一个程序及其数据在处理机上顺序执行时所发生的活动&#xf…

IDEA Maven Helper插件(详细使用教程)

一、引言 在写Java代码的时候,我们可能会出现Jar包的冲突的问题,这时候就需要我们去解决依赖冲突了,而解决依赖冲突就需要先找到是那些依赖发生了冲突,当项目比较小的时候,还比较依靠IEDA的【Diagrams】查看依赖关系&…

dom基本操作

1、style修改样式 基本语法&#xff1a; 元素.style.样式’值‘ 注意: 1.修改样式通过style属性引出 2.如果属性有-连接符&#xff0c;需要转换为小驼峰命名法 3.赋值的时候&#xff0c;需要的时候不要忘记加css单位 4.后面的值必须是字符串 <div></div> // 1、…

《Hadoop篇》------HDFS与MapReduce

目录 一、HDFS角色职责总结 二、CheckPoint机制 三、Mapreduce序列化 四、Mapper 4.1、官方介绍 4.2、Split计算 4.3、Split和block对应关系 4.4、启发式算法 五、MapTask整体的流程 六、压缩算法 6.1、压缩算法适用场景 6.2、压缩算法选择 6.2.1、Gzip压缩 6.2…

独家 | ChatGPT提高你日常工作的五个特点以及如何使用它来提高代码质量

翻译&#xff1a;陈超 校对&#xff1a;赵茹萱本文约3200字&#xff0c;建议阅读8分钟 本文介绍了ChatGPT提高日常工作的五个特点。ChatGPT已经完全改变了代码开发模式。然而&#xff0c;大多数软件开发者和数据专家们仍然不使用ChatGPT来完善——并简化他们的工作。这就是我们…

【Python】缺失值可视化工具库:missingno

文章目录一、前言二、下载二、使用介绍2.1 绘制缺失值条形图2.2 绘制缺失值热力图2.3 缺失值树状图三、参考资料一、前言 在我们进行机器学习或者深度学习的时候&#xff0c;我们经常会遇到需要处理数据集缺失值的情况&#xff0c;那么如何可视化数据集的缺失情况呢&#xff1…

MSR寄存器访问

1.介绍 MSR是CPU的一组64位寄存器&#xff0c;每个MSR都有它的地址值&#xff08;如下图所示&#xff09;&#xff0c;可以分别通过RDMSR 和WRMSR 两条指令进行读和写的操作。 如图中为8个P-state寄存器&#xff0c;地址分别为0xC001 0064 ~ 0xC001 006B&#xff0c;每个寄存…

FyListen 在 MVP 架构中的内存优化表现

FyListen 在 MVP 中的内存优化表现 本文只是分享个人开源框架的内存优化测试&#xff0c;你可以直接跳到最后&#xff0c;参考内存泄漏的分析过程&#xff01; 项目地址&#xff1a; https://github.com/StudyNoteOfTu/fylisten2-alpha1 由于使用到 AOP&#xff0c;所以直接…

【嵌入式开发】microcom安装与使用

microcom安装与使用1.安装2.使用3.用法4.测试三级目录1.安装 sudo apt-get install microcom -yQ&#xff1a;报错E: 在更改保留软件包的同时使用了 -y 选项&#xff0c;但没有搭配 --allow-change-held-packages. A&#xff1a;sudo apt-get install microcom -y --allow-cha…

软件测试面试复述,想知道你面试不过的原因吗?

最近有机会做一些面试工作&#xff0c;主要负责面试软件测试人员招聘的技术面试。 之前一直是应聘者的角色&#xff0c;经历了不少次的面试之后&#xff0c;多少也积累一点面试的经验&#xff0c;现在发生了角色转变。初次的面试就碰到个工作年限比我长的&#xff0c;也没有时…

PowerShell Install Office 2021 Pro Plus Viso Professional

前言 微软Office在很长一段时间内都是最常用和最受欢迎的软件。从小型创业公司到大公司,它的使用比例相当。它可以很容易地从微软的官方网站下载。但是,微软只提供安装程序,而不提供完整的软件供下载。这些安装文件通常比较小。下载并运行后,安装的文件将从后端服务器安装M…

5.1配置IBGP和EBGP

5.2.1实验1&#xff1a;配置IBGP和EBGP 实验目的 熟悉IBGP和EBGP的应用场景掌握IBGP和EBGP的配置方法 实验拓扑 实验拓扑如图5-1所示&#xff1a; 图5-1&#xff1a;配置IBGP和EBGP 实验步骤 IP地址的配置 R1的配置 <Huawei>system-view Enter system view, return …

Python每日一练(20230218)

目录​​​​​​​ 1. 旋转图像 2. 解码方法 3. 二叉树最大路径和 1. 旋转图像 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在原地旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像…

[LeetCode 1237]找出给定方程的正整数解

题目描述 题目链接&#xff1a;[LeetCode 1237]找出给定方程的正整数解 给你一个函数 f(x, y) 和一个目标结果 z&#xff0c;函数公式未知&#xff0c;请你计算方程 f(x,y) z 所有可能的正整数 数对 x 和 y。满足条件的结果数对可以按任意顺序返回。 尽管函数的具体式子未知…

Vue:@font-face引入外部字体

在项目开发中&#xff0c;我们经常会遇到想要优化字体font-family的问题&#xff0c;如下为默认字体样式&#xff0c;在大屏项目中看起来似乎有些呆板。 默认字体效果默认font属性尽管我们可以使用web安全字体&#xff0c;但是大多数场景下&#xff0c;例如&#xff1a;对于电子…

IOT2.5|第1章嵌入式系统概论|操作系统概述|嵌入式操作系统

目录 第1章&#xff1a; 嵌入式系统概论 1.嵌入式系统发展史 2.嵌入式系统定义* 3.嵌入式系统特点* 4.嵌入式处理器的特点 5.嵌入式处理分类 6.嵌入式系统的应用领域及嵌入式系统的发展趋势 第8章&#xff1a;Linux内核配置 1.内核概述 2.内核代码结构 第1章&#xf…