C语言volatile关键字详解
short flag;
void test()
{
do1();
while(flag==0);
do2();
}
这段程序等待内存变量flag的值变为1(怀疑此处是0,有点疑问,)之后才运行do2()。变量flag的值由别的程序更改,这个程序可能是某个硬件中断服务程序。例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。但是,编译器并不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。如果不幸进行了这样的优化,那么while循环就变成了死循环,因为寄存器的内容不可能被中断服务程序修改。为了让程序每次都读取真正flag变量的值,就需要定义为如下形式:
volatile short flag;
需要注意的是,没有volatile也可能能正常运行,但是可能修改了编译器的优化级别之后就又不能正常运行了。因此经常会出现debug版本正常,但是release版本却不能正常的问题。所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。
volatile的本意是“易变的”
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) do_something();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中断产生时,在main当中调用do_something函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致do_something永远也不会被调用。如果变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
二、volatile 的含义
volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用:
1 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器自己无法知道,volatile就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化,所以像下面的代码:
volatile int i = 1;
if (i > 0) ...
if的条件不会当作无条件真。
3 对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,然而对Memory Mapped IO的处理是不能这样优化的。
前面有人说volatile可以保证对内存操作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc。
对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。
你可能不知道在Pentium及后续CPU中,下面两组指令
inc jiffies
;;
mov jiffies, %eax
inc %eax
mov %eax, jiffies
作用相同,但一条指令反而不如三条指令快。
三、编译器优化 → C关键字volatile → memory破坏描述符zz
“memory”比较特殊,可能是内嵌汇编中最难懂部分。为解释清楚它,先介绍一下编译器的优化知识,再看C关键字volatile。最后去看该描述符。
1、编译器优化介绍
内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。以上是硬件级别的优化。再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。对常规内存进行优化的时候,这些优化是透明的,而且效率很好。由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux 提供了一个宏解决编译器的执行顺序问题。
void Barrier(void)
这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。
2、C语言关键字volatile
C语言关键字volatile(注意它是用来修饰变量而不是上面介绍的__volatile__)表明某个变量的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程,例如:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast<int*>(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。记住,C 编译器是没有线程概念的!这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label
3、Memory
有了上面的知识就不难理解Memory修改描述符了,Memory描述符告知GCC:
1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,它前面的指令都执行完毕
2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存。
如果汇编指令修改了内存,但是GCC 本身却察觉不到,因为在输出部分没有描述,此时就需要在修改描述部分增加“memory”,告诉GCC 内存已经被修改,GCC 得知这个信息后,就会在这段指令之前,插入必要的指令将前面因为优化Cache 到寄存器中的变量值先写回内存,如果以后又要使用这些变量再重新读取。
使用“volatile”也可以达到这个目的,但是我们在每个变量前增加该关键字,不如使用“memory”方便。
相关文章:

python储存数据的容器_Python基础四容器类数据
一、上周内容回顾int bool str 之间的互相转换int str:str(int)int(str) #字符串必须是数字组成int bool:bool(int):非零即TrueTrue --->1 Fasle --->0bool str:str-->bool #非空即Truestr:BIF自己去背吧二、列表why:1.取值费劲。2.对字符串…

Android 清单文件 详解
转载于:https://www.cnblogs.com/mohe/archive/2013/03/31/2991642.html

android屏幕分辨率详解 ldpi mdpi hdpi 程序UI自适应 《官方翻译》
2019独角兽企业重金招聘Python工程师标准>>> 看世界杯的空闲 时间,翻译一下 官方文档。分辨率 问题是大家都很关心的(720480会不会悲剧),而关于这个问题,android官方的文档无疑最有说服力。由于不是所有的人…

010 并发的三个特性
一 . 概述 在之前,我们使用synchronized关键词解决了原子性的操作,本节我们分析一个JVM内存模型导致的另外的两个问题. 二 . 可见性 为了加速线程的运行的速度,JVM的内存模型中设置了线程栈中的缓存,当一个线程使用了堆内存的数据的时候,首先会将这个数据缓存到线程栈之中, 当这…

LeetCode: Longest Consecutive Sequence
想到map了,可惜没想到用erase来节省空间,看了网上答案 1 class Solution {2 public:3 int longestConsecutive(vector<int> &num) {4 // Start typing your C/C solution below5 // DO NOT write int main() function6 …

python做测试书籍推荐_学习pytest应该观看的书籍?
这本书有中文版了pytest是动态编程语言Python专用的测试框架,它具有易于上手、功能强大、第三方插件丰富、效率高、可扩展性好、兼容性强等特点。《pytest测试实战》深入浅出地讲解了pytest的使用方法,尤其是具有特色的fixture的用法。作者通过丰富的测试…

路由器、路由与路由表
2019独角兽企业重金招聘Python工程师标准>>> 路由器、路由与路由表 路由器就是一台网络设备,它配备多个网络接口卡(NIC),能利用它的网络知识正确转发入口流量。 决定一个入口封包应当送给本地主机还是转发所需要的信息,以及在转发…

Hadoop虚拟机的jdk版本和本地eclipse的版本不一致怎么办
在本周学习Hadoop遇到了一个问题,困扰了半天,本人在安装Hadoop时是按照视频来的,结果发现Hadoop上的jdk版本和本地eclipse的版本不一致,导致本地的程序到处jar包传到虚拟机上运用Hadoop不能正常运行,如果你遇到相同的问…
操作符和表达式
一. 操作符 1. 算术操作符 - * / % 除了%之外其余的几个操作符既可以用于计算整型也可以用于计算浮点型数据,%只能计算整型数据,得到的结果是余数 2. 移位操作符 << 左移位操作符 >> 右移位操作符 <<左移…

kuayu react_react跨域解决方案
react跨域解决方案1.开发环境:reactaxioselement2.电脑系统:windows10专业版3.在使用react开发的过程中,我们总是会遇到跨域的问题,下面我来分享一下,在react中跨域处理方法!4.我使用的是axios向后台发送请求,安装axios:npm i axios --save5.安装代理中间件(http-proxy-middlew…

HDU 1429 胜利大逃亡(续) (BFS+位压缩)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid1429 胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3309 Accepted Submission(s): 1063 Problem DescriptionIgnatius再次…

Ext fucionchart插件
http://code.google.com/p/uxmedia/downloads/list转载于:https://www.cnblogs.com/jerome-rong/archive/2012/06/09/2543565.html

前端 ----jQuery的动画效果
03-jQuery动画效果 jQuery提供的一组网页中常见的动画效果,这些动画是标准的、有规律的效果;同时还提供给我们了自定义动画的功能。 显示动画 方式一: $("div").show(); 解释:无参数,表示让指定的元素直接显…

结构和联合--结构体内存和位段内存开辟规则
一. 结构的基本知识 聚合数据类型能够存储多个数据,C语言提供了两种类型的聚合数据类型,数组和结构。数组是相同的数据,结构是不同类型的数据聚合。结构也是一些值得集合,这些值成为它的成员,每个结构都有它的名字&a…

antd自定义分页器_自定义分页器
classPagination(object):def __init__(self, current_page, all_count, per_page_num10, pager_count11):"""封装分页相关数据:param current_page: 当前页:param all_count: 数据库中的数据总条数:param per_page_num: 每页显示的数据条数:param pager_count:…

.net实现跨页面传值
//一般用于向php,jsp等传值,因为跨语言session等不能共用,只有通过post提交 //下面演示的是服务器端控件提交 PostBackUrl"WebForm3.aspx"//这个页面只需要修改控件属性就能把值传给下一页面 protected void Page_Load(object send…

进程的同步、互斥以及PV原语
在处理进程间的同步与互斥问题时,我们离不开信号量和PV原语,使用这两个工具的目的在于打造一段不可分割不可中断的程序。应当注意的是,信号量和PV原语是解决进程间同步与互斥问题的一种机制,但并不是唯一的机制。 信号量ÿ…

ListT中,Remove和RemoveAt区别
Remove删除的是匹配的第一项。比如你的list里面有2个相同的项。那么就删除第一个。后面的不删除,找不到元素和删除失败都返回falseRemoveAt是删除索引下的项 转载于:https://www.cnblogs.com/mcyushao/p/9526208.html

vue 如何处理两个组件异步问题_Vue动态异步组件实现思路及其问题
前言:在vue官方资料中,我们可以可以很学会如何通过vue构建“动态组件”以及“异步组件”,然而,在官方资料中,并没有涉及到真正的“动态异步”组件,经过大量的时间研究和技术分析,我们给出目前比…

[转载] 七龙珠第一部——第004话 掳人的妖怪——乌龙
转载于:https://www.cnblogs.com/6DAN_HUST/archive/2013/04/07/3003566.html

如何解决资料下载下来为index.html和PHP文件的问题?
最近很多Down友反映,在下载中心下载资料时,明明是pdf、rar、zip格式的文件,下载完后怎么就变成index.html、php格式的文件了?既浪费了下载豆,文件还不能用,心疼啊!这是因为下载系统是动态获取的…

给大家推荐8个SpringBoot精选项目
前言 2017年,曾在自己的博客中写下这样一段话:有一种力量无人能抵挡,它永不言败生来倔强。有一种理想照亮了迷茫,在那写满荣耀的地方。 如今2018年已过大半,虽然没有大理想抱负,但是却有自己的小计划。下面…

点击Notification正确回调到之前已经放置在后台的Task中的对应Activity,而不是创建它的一个新实例...
NotificationManager notificationManager (NotificationManager)getSystemService(NOTIFICATION_SERVICE);Notification notification new Notification(R.drawable.logo_icon_16,"移动营销", System.currentTimeMillis());Intent intent new Intent(Intent.ACTI…

函数返回类的对象与拷贝构造函数
C中,如果我们在一个函数中,定义了一个类的对象,然后返回这个对象,在main函数中用一个对象去接受这个返回的对象的时候,这里面参与的函数调用大家可能不熟悉,这里通过程序和注释的方式给大家讲解一下。编译的…

ai条码插件免安装_ai条码插件2款下载|Barcode Toolbox插件+Barcode条码插件下载 - 偶要下载站...
本次一次性打包两款ai条码插件和大家分享,分别是Barcode Toolbox插件和Barcode脚本插件,支持Illustrator CS5~CC2015的条形码脚本!这两个插件不是一个插件,是有区别的两个插件。Barcode Toolbox是AI的一个非常有用的生成条码的插件…

GridView的DataKeyNames属性 转载的
偶今天用到这个了,转载 "事在人为"楼主的,原文地址: http://www.cnblogs.com/andhm/archive/2010/05/07/1730024.html DataKeyNames表示主键的列名,可以通过GridViewEntity.DataKeys[RowIndex]["ColumsName"]来获取他的值,当然它是…

反射 -- 通过字符串操作对象中的成员
getattr()setattr()hasattr()delattr()class C:def __init__(self, name):self.name namedef f(self):return Pythonobj C(Pyhton) get_name getattr(obj, name) get_func getattr(obj, f) get_func() hasattr(obj, name) setattr(obj, age, 10) delattr(obj, name)转载于:…

android默认exported_android:exported 属性详解
转自http://blog.csdn.net/watermusicyes/article/details/46460347昨天在用360扫描应用漏洞时,扫描结果,出来一个Android:exported属性,其实之前根本不知道这个属性,更不知道这个属性用来干嘛的,详情见下图࿱…

Chipset
Chipset 芯片组是一组集成电路(芯片)用于管理计算机处理器、内存和外设的数据流,通常位于主板上。 Northbridge (Memory Controller Hub) 北桥用来处理高速信号,负责CPU、RAM、AGP和PCI Express之间的通信。 Southbridge (I/O Con…

正确设置php-fpm和nginx防止网站被黑
2019独角兽企业重金招聘Python工程师标准>>> 核心总结:php-fpm 子进程所使用的用户,不能是网站文件所有者。 凡是违背这个原则,则不符合最小权限原则。 根据生产环境不断反馈,发现不断有 php网站被挂木马,绝…