上次面试被考官问到volatie变量,他说这个要结合cache来讲才能说的清,那位大侠给讲一下。
mingyanguo 回复于:2008-02-29 14:13:49
什么cache?我只觉得跟优化有关系。。。
zx_wing 回复于:2008-02-29 14:20:48
引用:原帖由 南海一钓叟 于 2008-2-29 13:44 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022088&ptid=1059006]
上次面试被考官问到volatie变量,他说这个要结合cache来讲才能说的清,那位大侠给讲一下。
他骗你的。
volatie变量和cache没有关系,只和内存有关系。
简单点说就是每次操作前从内存取值
有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ----> 操作 ---->写回内存
没有volatie修饰的变量,操作可能遵循(可能就是不是所有情况都如此):
从内存取值 ---> 放入寄存器 ----> 第一次操作 -----> 第二次操作(此时仍操作寄存器中的值) …… ---->第N次操作 ---->写回内存
[table=95%][tr][td][font=FixedSys][color=#000000]举个例子论述两者关系:
[color=#0000FF]int[/color] volatie i[color=#0000CC];[/color] [color=#FF9900]//全局变量,在其它地方会被修改
[/color]
[color=#0000FF]while[/color] [color=#0000CC]([/color]i[color=#0000CC])[/color]
[color=#0000CC]{[/color]
do_somethings[color=#0000CC]([/color][color=#0000CC])[/color][color=#0000CC];[/color]
[color=#0000CC]}[/color]
如果i没有被volatie修饰,当while循环执行时,另一段程序并发的执行了i[color=#0000CC]=[/color]0, 这个循环仍不会退出,因为每次循环都是检查寄存器中的值。
如果有volatie修饰,那么循环结束,因为循环每次检查i的时候,会先从内存把i读入寄存器,这个时候i在其它地方被赋0,则循环结束。[/color][/font][/td][/tr][/table]
tinywind 回复于:2008-02-29 15:00:30
有关系的,volatile变量的修改要求即时被所有cpu可见,所以会要求cache一致性。对一些weak memory model的cpu,还有对memory access order的限制。
zx_wing 回复于:2008-02-29 15:13:51
引用:原帖由 tinywind 于 2008-2-29 15:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022703&ptid=1059006]
有关系的,volatile变量的修改要求即时被所有cpu可见,所以会要求cache一致性。对一些weak memory model的cpu,还有对memory access order的限制。
不不不,这个一致性并非是volatile引起的,而是从内存取值都会有的问题。
假设有两个CPU,CPU1和CPU2, 一个变量A(不管有没有volatile修饰)位于地址addr1
CPU1修改了变量A,同步回内存,这个时候可能它并没有直接被写到内存,而是放到CPU1的缓存中了。这个时候CPU2要从内存中读入A的值,会向总线提交地址addr1。由于一个总线的translation阶段有个snoop(监听)阶段,CPU1就会得知CPU2在请求位于地址addr1的变量A的值,则CPU1会把变量A的值同步回内存,从而CPU2从内存读到的A的值就是最新的。从而保证了一致性。
所以这个一致性和volatie没有关系,只要是变量在修改后写回内存,硬件机制就会保证它们的一致性。
故前面提到的volatie变量修改顺序中最后一步:
从内存取值 ---> 放入寄存器 ----> 操作 ---->写回内存
就做了这个一致性保证。
mingyanguo 回复于:2008-02-29 15:22:35
引用:原帖由 tinywind 于 2008-2-28 23:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022703&ptid=1059006]
有关系的,volatile变量的修改要求即时被所有cpu可见,所以会要求cache一致性。对一些weak memory model的cpu,还有对memory access order的限制。
这个access order应该不是能用volatile可以解决的。。。
南海一钓叟 回复于:2008-02-29 15:24:00
多谢zx_wing 和tinywind两位大侠给的解释,本来一头雾水,现在终于有点了解了,但是有些不明白的就是zx_wing说到了两个CPU,这个我不是很理解了,
zx_wing 回复于:2008-02-29 15:32:03
引用:原帖由 南海一钓叟 于 2008-2-29 15:24 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022897&ptid=1059006]
多谢zx_wing 和tinywind两位大侠给的解释,本来一头雾水,现在终于有点了解了,但是有些不明白的就是zx_wing说到了两个CPU,这个我不是很理解了,
这个其实和volatie没有关系,不理解就算了,这个是前端总线怎么在多CPU情况下如何保证内存一致性的方法。
我说这个主要是想说明volatie和cache是没有关系的。
如果volatie可以指定编译器执行内存同步(也就是cache同步),那么一个简单的实验就可以检验。
写一个小程序,修改了volatie变量,编译成汇编,然后看修改 volatie 变量的下一条指令是否带有隐式的内存同步功能,或显式的执行了内存屏障指令。很明显可以看到,编译器实际上是没有这样做的。
我认为LZ只要理解了我第一个回帖中的内容即可。
[ 本帖最后由 zx_wing 于 2008-2-29 15:34 编辑 ]
net_robber 回复于:2008-02-29 15:32:44
学习,学习
zx_wing 回复于:2008-02-29 15:33:18
引用:原帖由 mingyanguo 于 2008-2-29 15:22 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022887&ptid=1059006]
这个access order应该不是能用volatile可以解决的。。。
不能解决,这个要靠带有内存屏障功能的指令才行,volatile不能指定编译器生成这样的指令。
alazer 回复于:2008-02-29 15:37:58
真是受益匪浅啊!!!!
tinywind 回复于:2008-02-29 15:41:21
引用:原帖由 zx_wing 于 2008-2-29 15:32 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022971&ptid=1059006]
这个其实和volatie没有关系,不理解就算了,这个是前端总线怎么在多CPU情况下如何保证内存一致性的方法。
我说这个主要是想说明volatie和cache是没有关系的。
如果volatie可以指定编译器执行内存同步(也 ...
你用一台安腾的机器试试看就知道了,或者用google搜索load acquire/store release
mingyanguo 回复于:2008-02-29 15:46:39
引用:原帖由 tinywind 于 2008-2-28 23:41 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023057&ptid=1059006]
你用一台安腾的机器试试看就知道了,或者用google搜索load acquire/store release
你们讨论的应该不是一个问题。。。我估计你说的是 memory fence...这个与volatile应该是没什么关系的。。。
tgbvc 回复于:2008-02-29 15:54:38
volatie 只是编译器优化功能的关键字
zx_wing 回复于:2008-02-29 16:02:44
引用:原帖由 tinywind 于 2008-2-29 15:41 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023057&ptid=1059006]
你用一台安腾的机器试试看就知道了,或者用google搜索load acquire/store release
呵呵,正好问到我了。
IA64是我最熟悉的架构。
嘿嘿,我知道兄台说的是ld.acq和st.rel吧。
是,IA64是这样实现的,它本来就有这样的指令可以保证。
我没想到论坛上有用IA64的,所以没有提这个。
但实际上volatie确实和cache没有任何关系,它本意只是从内存取值。而从内存取值,就涉及到了cache同步而已。只不过正好IA64有这样的指令,编译器用来实现了volatie而已,并非volatie指定编译这样做。
[ 本帖最后由 zx_wing 于 2008-2-29 16:04 编辑 ]
zx_wing 回复于:2008-02-29 16:03:54
引用:原帖由 mingyanguo 于 2008-2-29 15:46 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023100&ptid=1059006]
你们讨论的应该不是一个问题。。。我估计你说的是 memory fence...这个与volatile应该是没什么关系的。。。
呵呵,IA64比较特殊,正好用于实现volatie的指令本身就是用来ord accessing的,所以tinywind 就提到这个了。
mingyanguo 回复于:2008-02-29 16:07:23
引用:原帖由 zx_wing 于 2008-2-29 00:03 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023228&ptid=1059006]
呵呵,IA64比较特殊,正好用于实现volatie的指令本身就是用来ord accessing的,所以tinywind 就提到这个了。
噢,多谢。不了解这个。
南海一钓叟 回复于:2008-02-29 16:13:09
还是CU上面高手多啊,在其他几个坛子提了半天,都没有几个让我明白的答案,以后就觉得泡CU了
flw2 回复于:2008-02-29 16:14:46
volatile变量的使用,就是说编译器确保变量和内存是同步的
而是内存与CPU的cache对于这个“同步的”是不透明的,这也就是IA64需要这样的指令来实现volatile(IA32就不需也没有显示的指令),因为不使用这样的指令就不是volatile的了
编译器知道cache这个东西,在实现volatile的时候。
对不?
南海一钓叟 回复于:2008-02-29 16:15:26
还是CU上面高手多啊,在几个坛子里面发了都没有几个让我明白的回答,以后决定就泡CU了:)
flw2 回复于:2008-02-29 16:17:35
我的意思是对于zx_wing 说的正好,不是正好,而是必须的
zx_wing 回复于:2008-02-29 16:18:29
引用:原帖由 flw2 于 2008-2-29 16:14 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023312&ptid=1059006]
volatile变量的使用,就是说编译器确保变量和内存是同步的
而是内存与CPU的cache对于这个“同步的”是不透明的,这也就是IA64需要这样的指令来实现volatile(IA32就不需也没有显示的指令),因为不使用这样的指 ...
对对,我觉得总结的太好了。
volatile应该是编译器保证变量同步到内存,至于cache的同步由架构自行实现。IA64不用上面提到的那两个指令也可以实现,因为有其它串行化指令,只是用那两条指令最方便。
所以那句话,cache的同步由架构自行决定,和volatile无关。
flw2 回复于:2008-02-29 16:19:50
引用:原帖由 zx_wing 于 2008-2-29 16:18 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023337&ptid=1059006]
对对,我觉得总结的太好了。
volatile应该是编译器保证变量同步到内存,至于cache的同步由架构自行实现。IA64不用上面提到的那两个指令也可以实现,因为有其它串行化指令,只是用那两条指令最方便。
所以那 ...
恩,我不了解IA64,以为只有那么做才行
南海一钓叟 回复于:2008-02-29 16:21:46
也就是说cache的内容同步有硬件机制完成,而volatile则是编译器优化的时候保证其变量的同步?
我这样理解可以不?
zx_wing 回复于:2008-02-29 16:31:56
引用:原帖由 南海一钓叟 于 2008-2-29 16:21 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023357&ptid=1059006]
也就是说cache的内容同步有硬件机制完成,而volatile则是编译器优化的时候保证其变量的同步?
我这样理解可以不?
可以吧,变量值和内存同步即可
tinywind 回复于:2008-02-29 16:35:15
volatile肯定是给compiler使用的,这点没有疑议。一般只要cache一致了就可以了,不需要一定写回内存。
不太清楚c标准中对volatile的access order有没有规定,在java的memory model是定义了volatile变量和normal load/save的次序的,哪些可以reorder哪些不可以都有定义。否则的话有些lock free的算法会有问题。
liulang0808 回复于:2008-02-29 16:41:37
学习一下
zx_wing 回复于:2008-02-29 16:42:51
引用:原帖由 tinywind 于 2008-2-29 16:35 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023457&ptid=1059006]
volatile肯定是给compiler使用的,这点没有疑议。一般只要cache一致了就可以了,不需要一定写回内存。
不太清楚c标准中对volatile的access order有没有规定,在java的memory model是定义了volatile变量和norma ...
一般只要cache一致了就可以了,不需要一定写回内存
有误,cache一致必定写回内存。这个是北桥规定的。也就是说不能直接从其它CPU的cache中读值。
tinywind 回复于:2008-02-29 16:45:35
引用:原帖由 zx_wing 于 2008-2-29 16:42 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023525&ptid=1059006]
一般只要cache一致了就可以了,不需要一定写回内存
有误,cache一致必定写回内存。这个是北桥规定的。也就是说不能直接从其它CPU的cache中读值。
是所有系统都这样吗,还是特定的系统。似乎这是个很傻的设计,在现在多核的cpu中还要从内存中走一圈吗。
zx_wing 回复于:2008-02-29 16:51:36
引用:原帖由 tinywind 于 2008-2-29 16:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8023549&ptid=1059006]
是所有系统都这样吗,还是特定的系统。似乎这是个很傻的设计,在现在多核的cpu中还要从内存中走一圈吗。
应该都是这样的。
并不是说要先写回内存再读出来,举个例子:
CPU2读一个地址的内容,CPU1发现应该做cache同步,于是会把这个地址的最新内容放到总线上,北桥上的内存控制器会做两个动作,一个是把这个最新的内容给CPU2,另一个动作会把最新内容同步到内存中对应的地址上去。
所以说cache同步必定带来内存的同步。
It'sGifted 回复于:2008-02-29 19:35:45
原来我也不懂,不过现在懂了
bobozhang 回复于:2008-02-29 20:04:21
没想到成都还有这么厉害的高手哈,佩服!
flw2 回复于:2008-02-29 20:12:36
发现一个小问题,和主题无关,曾经讨论过
3楼 do_someting(如果是函数)可能会修改全局的i,而编译器不可能去检查这个事实,所以
while(i)
do_something();
不是例子
while(i) {
/**/
}
:outu:
想飞的蜗牛 回复于:2008-02-29 20:14:13
终于明白snoop的意义...
swanrobin 回复于:2008-03-01 23:07:33
关于 volatile 我觉得这样的解析最容易理解:
如果编译器在代码中发现对同一地址的两次访问之间,没有对该地址进行写操作,那么编译器将优化为第一次寻址读该地址时取得的值作为第二次寻址的值,而并不是再做第二次物理上的 I/O 寻址操作。volatile 关键字指示编译器进行强制 I/O 寻址,因为编译器那样的优化,可能并不是我们真正期望的,譬如那个地址上连接着一个传感器上的寄存器,那么实际上,可能该寄存器的值是被传感器自身不断刷新的。因此,我们必要要求CPU每次都进行 I/O 操作。
hotjuly 回复于:2008-03-02 12:31:50
楼主应聘的是嵌入式的嘛
sirouni 回复于:2008-03-02 18:29:48
Do you volatile? should you?
by Dr. Kevin P. Dankwardt
Volatile is an ANSI C type modifier that is frequently needed in C code that is part of signal/interrupt handlers, threaded code, and other kernel code, including device drivers. In general, any data that may be undated asynchronously should be declared to be volatile. Incidentally, this issue is [size=7]not related to[/size] CPU caches, except that re-loading of variables into registers may involve cache hits or misses.
Why Use Volatile?
The reason to use volatile is to ensure that the compiler generates code to re-load a data item each time it is referenced in your program. Without volatile, the compiler may generate code that merely re-uses the value it already loaded into a register.
Volatile advises the compiler that the data may be modified in a manner that may not be determinable by the compiler. This could be, for example, when a pointer is mapped to a device's hardware registers. The device may independently change the values unbeknownst to the compiler.
With gcc the -O2 option is normally required to see the effect of not using volatile. Without -O2 or greater optimization, the compiler is likely to re-load registers each time a variable is referenced, anyway. Don't blame the optimizer if a program gets incorrect results because the program does not use volatile where required.
For example, if two threads share a variable, sum, and one or both threads modify it, then the other thread may use a stale value in a register instead of going back to memory to get the new value. Instead, each time the thread references sum, it must be re-loaded. The way to insure this occurs in ANSI C is to declare sum to be volatile.
Example 1.
[table=95%][tr][td][font=FixedSys][color=#000000] [color=#ff9900]// Program to measure the difference between volatile and not
[/color]
[color=#ff9900]// written by Kevin P. Dankwardt [email=k@kcomputing.com]k@kcomputing.com[/email]
[/color]
[color=#ff9900]// 3 March 2005
[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color]stdio[color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color][color=#ff0000]signal[/color][color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color]sched[color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color]sys[color=#0000cc]/[/color][color=#ff0000]time[/color][color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000cc]#[/color][color=#ff0000]ifndef[/color] [color=#0000ff]VOLATILE[/color]
[color=#0000cc]#[/color][color=#ff0000]define[/color] [color=#0000ff]VOLATILE[/color]
[color=#0000cc]#[/color][color=#ff0000]endif[/color]
[color=#0000ff]VOLATILE[/color] [color=#0000ff]int[/color] total[color=#0000cc]=[/color]0[color=#0000cc];[/color]
[color=#0000ff]void[/color] handle[color=#0000cc]([/color][color=#0000ff]int[/color] signo[color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#0000ff]if[/color] [color=#0000cc]([/color]signo [color=#0000cc]=[/color][color=#0000cc]=[/color] SIGALRM[color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#ff0000]printf[/color][color=#0000cc]([/color][color=#ff00ff]"Total = %d\n"[/color][color=#0000cc],[/color]total[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]exit[/color][color=#0000cc]([/color]0[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000cc]}[/color]
total[color=#0000cc]+[/color][color=#0000cc]+[/color][color=#0000cc];[/color]
[color=#0000cc]}[/color]
[color=#0000ff]int[/color] main [color=#0000cc]([/color][color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#0000ff]VOLATILE[/color] [color=#0000ff]unsigned[/color] x[color=#0000cc]=[/color]0[color=#0000cc];[/color]
[color=#0000ff]VOLATILE[/color] [color=#0000ff]int[/color] i[color=#0000cc],[/color]j[color=#0000cc];[/color]
[color=#0000ff]struct[/color] sched_param param[color=#0000cc];[/color]
[color=#0000ff]struct[/color] [color=#ff0000]itimerval[/color] val[color=#0000cc];[/color]
val[color=#0000cc].[/color]it_interval[color=#0000cc].[/color]tv_sec [color=#0000cc]=[/color] 0[color=#0000cc];[/color]
val[color=#0000cc].[/color]it_interval[color=#0000cc].[/color]tv_usec [color=#0000cc]=[/color] 10000[color=#0000cc];[/color] [color=#ff9900]// 10 ms
[/color]
val[color=#0000cc].[/color]it_value[color=#0000cc].[/color]tv_sec [color=#0000cc]=[/color] 0[color=#0000cc];[/color]
val[color=#0000cc].[/color]it_value[color=#0000cc].[/color]tv_usec [color=#0000cc]=[/color] 10000[color=#0000cc];[/color] [color=#ff9900]// 10 ms
[/color]
[color=#ff0000]setitimer[/color][color=#0000cc]([/color][color=#ff0000]ITIMER_VIRTUAL[/color][color=#0000cc],[/color] [color=#0000cc]&[/color]val[color=#0000cc],[/color] [color=#ff0000]NULL[/color][color=#0000cc])[/color][color=#0000cc];[/color]
param[color=#0000cc].[/color]sched_priority [color=#0000cc]=[/color] 99[color=#0000cc];[/color]
[color=#0000ff]if[/color] [color=#0000cc]([/color]sched_setscheduler[color=#0000cc]([/color]0[color=#0000cc],[/color] SCHED_RR[color=#0000cc],[/color] [color=#0000cc]&[/color]param[color=#0000cc])[/color] [color=#0000cc]=[/color][color=#0000cc]=[/color][color=#0000cc]-[/color]1[color=#0000cc])[/color] [color=#0000cc]{[/color]
[color=#ff0000]perror[/color][color=#0000cc]([/color][color=#ff00ff]"setting priority"[/color][color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]exit[/color][color=#0000cc]([/color]1[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000cc]}[/color]
[color=#0000cc]#[/color][color=#ff0000]define[/color] BIGNUM [color=#0000cc]([/color]1000[color=#0000cc])[/color]
alarm[color=#0000cc]([/color]10[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]signal[/color][color=#0000cc]([/color]SIGALRM[color=#0000cc],[/color]handle[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]signal[/color][color=#0000cc]([/color]SIGVTALRM[color=#0000cc],[/color]handle[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000ff]while[/color] [color=#0000cc]([/color]total [color=#0000cc]<[/color] 100 [color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#0000ff]for[/color] [color=#0000cc]([/color]i[color=#0000cc]=[/color]0[color=#0000cc];[/color] i[color=#0000cc]<[/color]BIGNUM[color=#0000cc];[/color] i[color=#0000cc]+[/color][color=#0000cc]+[/color][color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#0000ff]for[/color] [color=#0000cc]([/color]j[color=#0000cc]=[/color]0[color=#0000cc];[/color] j[color=#0000cc]<[/color]BIGNUM[color=#0000cc];[/color] j[color=#0000cc]+[/color][color=#0000cc]+[/color][color=#0000cc])[/color]
x [color=#0000cc]=[/color] x[color=#0000cc]+[/color]j [color=#0000cc];[/color]
[color=#0000cc]}[/color]
[color=#0000cc]}[/color]
[color=#ff0000]printf[/color][color=#0000cc]([/color][color=#ff00ff]"x = %u\n"[/color][color=#0000cc],[/color]x[color=#0000cc])[/color][color=#0000cc];[/color] [color=#ff9900]// so optimizers doesn't throw away the loop
[/color]
[color=#ff0000]printf[/color][color=#0000cc]([/color][color=#ff00ff]"total = %d\n"[/color][color=#0000cc],[/color]total[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000cc]}[/color][/color][/font][/td][/tr][/table]
The use of volatile can be required to get correct answers. For example the program wrong will give incorrect results when it is compiled -O2 and without volatile. This slightly obtuse program is designed to stop after 100 ticks of an interval timer that ticks at 100Hz and print the value of the variable total. The tick count is incremented in the signal handler. When the count gets to 100, the program should terminate. If the tick count does not get to 100 within 10 seconds then an alarm goes off and the program terminates.
By compiling the program as:
gcc -O2 -DVOLATILE=volatile wrong.c -o wrong_v
you will see, (unless your program is preempted for quite a while), that the count gets to 100 and the program terminates as designed. With the program compiled as
gcc -O2 wrong.c -o wrong_nv
you will see, that the count becomes greater than 100 as shown when the handler prints it, but, the while loop does not terminate.
Incidentally, attempts to determine what is happening may thwart your efforts. For example, a function call, such as to printf(), or the use of a breakpoint, in the loop, will likely spill and re-load the registers.
Syntax
The keyword volatile is similar to the const keyword. Volatile is used to modify a type. Thus an int, const int, pointer, etc. may be declared to be volatile. In addition, a point may be declared to be a pointer to volatile. A pointer to volatile means that the data to which the pointer refers is volatile as opposed to the pointer itself. Of course, both the pointer and to which it refers, may be declared to be volatile.
To declare a volatile int do:
volatile int v;
and to declare vp to be a pointer to a volatile int do:
volatile int *vp;
Since deciphering C declarations can be difficult you may want to consult the C declaration chapter in the Sun manual. This manual references the Decoder Flowchart that can be used to help decipher declarations.
In addition, Linux may have the cdecl(1) program that can be used to translate C declarations to English, as for example, in
echo 'explain volatile int *v' | cdecl
which will answer with
declare v as a pointer to volatile int
Reading C declarations is made simpler when you realize that they are written boustrophedonically. Of course, even knowing the definition of boustrophedonically doesn't really help. The idea is that C declarations are interpreted based on the tricky precedence of operators such as "*", "[]", and "()".
Performance Issues
In some sense, volatile is the opposite of register. Thus, one can expect to lose performance. This means don't use volatile when it is not needed.
Example 2.
[table=95%][tr][td][font=FixedSys][color=#000000] [color=#ff9900]// Program to measure the difference between volatile and not
[/color]
[color=#ff9900]// written by Kevin P. Dankwardt [email=k@kcomputing.com]k@kcomputing.com[/email]
[/color]
[color=#ff9900]// 3 March 2005
[/color]
[color=#ff9900]// compile with -O2 flag to see volatile make a difference
[/color]
[color=#ff9900]// compile with -DVOLATILE=volatile to use volatile.
[/color]
[color=#ff9900]// 3 March 2005
[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color]stdio[color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color][color=#ff0000]signal[/color][color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000cc]#[/color][color=#ff0000]include[/color] [color=#0000cc]<[/color]sched[color=#0000cc].[/color]h[color=#0000cc]>[/color]
[color=#0000ff]int[/color] total[color=#0000cc]=[/color]0[color=#0000cc];[/color]
[color=#0000cc]#[/color][color=#ff0000]ifndef[/color] [color=#0000ff]VOLATILE[/color]
[color=#0000cc]#[/color][color=#ff0000]define[/color] [color=#0000ff]VOLATILE[/color]
[color=#0000cc]#[/color][color=#ff0000]endif[/color]
[color=#0000ff]void[/color] handle[color=#0000cc]([/color][color=#0000ff]int[/color] signo[color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#0000ff]int[/color] t[color=#0000cc]=[/color]total[color=#0000cc];[/color]
[color=#0000ff]char[/color] type[color=#0000cc][[/color]100[color=#0000cc]][/color][color=#0000cc];[/color]
[color=#ff0000]printf[/color][color=#0000cc]([/color][color=#ff00ff]"%d\n"[/color][color=#0000cc],[/color] t[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]exit[/color][color=#0000cc]([/color]0[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000cc]}[/color]
[color=#0000ff]int[/color] main [color=#0000cc]([/color][color=#0000cc])[/color]
[color=#0000cc]{[/color]
[color=#0000ff]VOLATILE[/color] [color=#0000ff]int[/color] x[color=#0000cc]=[/color]0[color=#0000cc];[/color]
[color=#0000ff]VOLATILE[/color] [color=#0000ff]int[/color] i[color=#0000cc],[/color]j[color=#0000cc];[/color]
[color=#0000ff]struct[/color] sched_param param[color=#0000cc];[/color]
param[color=#0000cc].[/color]sched_priority [color=#0000cc]=[/color] 99[color=#0000cc];[/color]
[color=#0000ff]if[/color] [color=#0000cc]([/color]sched_setscheduler[color=#0000cc]([/color]0[color=#0000cc],[/color] SCHED_RR[color=#0000cc],[/color] [color=#0000cc]&[/color]param[color=#0000cc])[/color] [color=#0000cc]=[/color][color=#0000cc]=[/color][color=#0000cc]-[/color]1[color=#0000cc])[/color] [color=#0000cc]{[/color]
[color=#ff0000]perror[/color][color=#0000cc]([/color][color=#ff00ff]"setting priority"[/color][color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]exit[/color][color=#0000cc]([/color]1[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000cc]}[/color]
[color=#0000cc]#[/color][color=#ff0000]define[/color] BIGNUM [color=#0000cc]([/color]1span[color=#0000cc]>[/color]
alarm[color=#0000cc]([/color]10[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#ff0000]signal[/color][color=#0000cc]([/color]SIGALRM[color=#0000cc],[/color]handle[color=#0000cc])[/color][color=#0000cc];[/color]
[color=#0000ff]for[/color] [color=#0000cc]([/color]i[color=#0000cc]=[/color]0[color=#0000cc];[/color] i[color=#0000cc]<[/color]BIGNUM[color=#0000cc];[/color] i[color=#0000cc]+[/color][color=#0000cc]+[/color][color=#0000cc])[/color]
[color=#0000cc]{[/color]
total[color=#0000cc]+[/color][color=#0000cc]+[/color][color=#0000cc];[/color]
[color=#0000ff]for[/color] [color=#0000cc]([/color]j[color=#0000cc]=[/color]0[color=#0000cc];[/color] j[color=#0000cc]<[/color]BIGNUM[color=#0000cc];[/color] j[color=#0000cc]+[/color][color=#0000cc]+[/color][color=#0000cc])[/color]
x [color=#0000cc]=[/color] x[color=#0000cc]+[/color]j [color=#0000cc];[/color]
[color=#0000cc]}[/color]
[color=#ff0000]printf[/color][color=#0000cc]([/color][color=#ff00ff]"x = %d\n"[/color][color=#0000cc],[/color]x[color=#0000cc])[/color][color=#0000cc];[/color] [color=#ff9900]// so optimizers doesn't throw away the loop
[/color]
[color=#0000cc]}[/color][/color][/font][/td][/tr][/table]
In our performance example we can see the difference that volatile may make. If we compile this program with and without VOLATILE defined as volatile we see an average number of iterations of almost 5,000 for the volatile case and almost 20,000 for the non-volatile case. Yikes! Remember that we must compile both of them with the -O2 option. (These iteration counts were made on a 400Mhz AMD-K6.)
Linux Examples
The use of the volatile keyword is common in the Linux kernel source. For example, of the 10,607 .c and .h files in the Fedora Core 1, Linux kernel source directory, 1,694 have the string "volatile" in them somewhere. As an example, the file drivers/net/eepro.c uses volatile in three places.
385: volatile s32 cmd_status; /* All command and status fields. */
392: volatile s32 status;
764: volatile s32 *self_test_results;
Generated Code
By examining the code generated by the compiler one can see the difference volatile makes. In this simple example we can see the x86 assembly language when volatile is used and when volatile is not used.
Quiz Yourself
What is volatile in each of the following examples? Are they all legal declarations?
1) volatile int *s1;
2) int* volatile s2;
3) volatile int* volatile s3;
4) const volatile int * volatile s4;
5) volatile int * (*f)(volatile int *);
Check your answers.
Summary
The volatile keyword is relatively unknown. There are times when its use is required for correct operation of C/C++ programs. In general, whenever a variable may be altered asynchronously, such as by a signal handler or mapped hardware, the variable must be declared to be volatile.
Since volatile prevents re-using values in registers, volatile comes with a performance penalty that can be substantial.
Also, since declarations involving volatile can be difficult to decipher you may want to use cdecl(1).
原文链接 [url=http://www.linuxdevices.com/articles/AT5980346182.html]http://www.linuxdevices.com/articles/AT5980346182.html
[ 本帖最后由 sirouni 于 2008-3-2 18:45 编辑 ]
srdrm 回复于:2008-03-03 10:43:49
volatile 跟以前的 register 相反. register 告诉编译器尽量将变量放到寄存器中使用, 而volatile 强制将更改后的值写回内存(无论是cache还是内存). 如果不写回内存, 对于一些全局共享的变量, 可能导致不一致问题.
[ 本帖最后由 srdrm 于 2008-3-3 10:47 编辑 ]
sohu2000000 回复于:2008-03-03 11:15:24
我的博客里面有几篇关于volatile变量的文章
可以去看看:-)
http://blog.chinaunix.net/u1/52454/showart_487024.html
南海一钓叟 回复于:2008-03-03 11:19:00
OK,谢谢咯
zhlyp 回复于:2008-03-03 15:24:58
请问http://blog.chinaunix.net/u1/52454/showart_486989.html这个帖子中的例1
例1.
void main (void)
{
volatile int i;
int j;
i = 1; //1 不被优化 i=1
i = 2; //2 不被优化 i=1
i = 3; //3 不被优化 i=1
j = 1; //4 被优化
j = 2; //5 被优化
j = 3; //6 j = 3
}
[size=3][color=Red]i[/color]的最终结果是多少?1?还是3?[/size]
[ 本帖最后由 zhlyp 于 2008-3-3 15:28 编辑 ]
zhlyp 回复于:2008-03-03 15:31:12
我在linux下编译的结果i=3
但是不理解楼上例子的意思
zx_wing 回复于:2008-03-03 15:45:26
引用:原帖由 zhlyp 于 2008-3-3 15:31 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8032536&ptid=1059006]
我在linux下编译的结果i=3
但是不理解楼上例子的意思
这就是volatie的另一个功能,主要用于嵌入式(嵌入式我不懂,说错误怪)
就是体现一个时序。
这里i的变化有3个过程, i = 1, i =2, i =3 ,编译器会把这个过程体现出来,汇编指令变为如下序列:
i 赋值为1
i 赋值为2
i 赋值为3
最终结果当然是3,只是体现了i变化的一个过程。
如果没有volatie修饰,编译器会把前两次赋值忽略掉,也就是所谓的优化掉,汇编指令变成一条:
i 赋值为3
zhlyp 回复于:2008-03-03 15:56:04
引用:原帖由 zx_wing 于 2008-3-3 15:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8032627&ptid=1059006]
这就是volatie的另一个功能,主要用于嵌入式(嵌入式我不懂,说错误怪)
就是体现一个时序。
这里i的变化有3个过程, i = 1, i =2, i =3 ,编译器会把这个过程体现出来,汇编指令变为如下序列:
i 赋值为1 ...
奥,谢谢
那就是说那个例子应该错了
应该改成
[size=3][table=95%][tr][td][font=FixedSys][color=#000000] i [color=#0000CC]=[/color] 1[color=#0000CC];[/color] [color=#FF9900]//1 不被优化 i=1
[/color]
i [color=#0000CC]=[/color] 2[color=#0000CC];[/color] [color=#FF9900]//2 不被优化 i=2
[/color]
i [color=#0000CC]=[/color] 3[color=#0000CC];[/color] [color=#FF9900]//3 不被优化 i=3
[/font][/td][/tr][/table][/size]
[ 本帖最后由 zhlyp 于 2008-3-3 15:57 编辑 ]
zx_wing 回复于:2008-03-03 16:04:19
引用:原帖由 zhlyp 于 2008-3-3 15:56 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8032699&ptid=1059006]
奥,谢谢
那就是说那个例子应该错了
应该改成
i = 1; //1 不被优化 i=1
i = 2; //2 不被优化 i=2
i = 3; //3 不被优化 i=3
对,我估计是笔误
wangqi0021 回复于:2008-03-03 17:58:43
受教了。 比原来理解的更深一些
RobinsonNie 回复于:2008-03-03 18:52:40
幸好手边有keil,要不真颠覆自己对volatile的理解了,调试过程如下。
说明:mov操纵片内的256byte的RAM, Memory View中d:0x0008显示的便为片内RAM0x0008处值。
RobinsonNie 回复于:2008-03-03 18:57:42
这哥们是不是调程序时看程序存储器0x0008处值了,那倒真是一直是1.
guozhan1722 回复于:2008-03-04 12:50:55
一般来说,如果做device driver 的程序员,对这个问题分得很清楚。volatile 是用来防止编译器对代码进行优化的,定义成volatile的变量,不会被分配临时的寄存器来优化。所以,对外设来说,读的时候,读到的是内存的即时值。可以通过反汇编来比较一下源代码。cacha 是cpu读写时的一块缓冲区,一般对内存的操作是enable cacha的,但对外设必须对cacha进行单独的操作或关掉cacha.一般地,读的时候是invalidate cacha.写的时候是flush cacha。如果有MMU的话,可以自己配一块内存,单独对cacha 进行管理。
虎皮尖椒 回复于:2008-03-04 14:36:01
mark一下,拜服各位高手。
南海一钓叟 回复于:2008-03-04 16:29:39
刚才在sun网站上找到一个关于volatile变量的文章,感觉说的挺好的了。
[color=Red]
Declaring a Variable Volatile[/color]
volatile is a keyword that must be applied when declaring any variable that will reference a device register. Without the use of volatile, the compile-time optimizer can inadvertently delete important accesses. Neglecting to use volatile might result in bugs that are difficult to track down.
The correct use of volatile is necessary to prevent elusive bugs. The volatile keyword instructs the compiler to use exact semantics for the declared objects, in particular, not to remove or reorder accesses to the object. Two instances where device drivers must use the volatile qualifier are:
When data refers to an external hardware device register, that is, memory that has side effects other than just storage. Note, however, that if the DDI data access functions are used to access device registers, you do not have to use volatile.
When data refers to global memory that is accessible by more than one thread, that is not protected by locks, and that relies on the sequencing of memory accesses. Using volatileconsumes fewer resources than using lock.
The following example uses volatile. A busy flag is used to prevent a thread from continuing while the device is busy and the flag is not protected by a lock:
while (busy) {
/* do something else */
}The testing thread will continue when another thread turns off the busy flag:
busy = 0;Because busy is accessed frequently in the testing thread, the compiler can potentially optimize the test by placing the value of busy in a register and test the contents of the register without reading the value of busy in memory before every test. The testing thread would never see busy change and the other thread would only change the value of busy in memory, resulting in deadlock. Declaring the busy flag as volatile forces its value to be read before each test.
--------------------------------------------------------------------------------
Note –
An alternative to the busy flag is to use a condition variable. See Condition Variables in Thread Synchronization.
--------------------------------------------------------------------------------
When using the volatile qualifier, avoid the risk of accidental omission. For example, the following code
struct device_reg {
volatile uint8_t csr;
volatile uint8_t data;
};
struct device_reg *regp;is preferable to the next example:
struct device_reg {
uint8_t csr;
uint8_t data;
};
volatile struct device_reg *regp;Although the two examples are functionally equivalent, the second one requires the writer to ensure that volatile is used in every declaration of type struct device_reg. The first example results in the data being treated as volatile in all declarations and is therefore preferred. As mentioned above, using the DDI data access functions to access device registers makes qualifying variables as volatile unnecessary.
sjh_311 回复于:2008-03-04 16:38:37
mark一下,zx_wing讲的不错
mymtom 回复于:2008-03-04 20:44:49
高手真多啊!
以前俺只知道大意是让编译器不要进行优化(即不要使用寄存器已经缓存的值, 而要访问内存).
典型的例子是IO端口映射的地址, 共享内存等.
yangtou 回复于:2008-03-04 21:54:58
cache一致不一定要写回内存,甚至不需要通过总线,比如双核系统共享写回式 L2 cache
zx_wing 回复于:2008-03-04 21:58:27
引用:原帖由 yangtou 于 2008-3-4 21:54 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8038994&ptid=1059006]
cache一致不一定要写回内存,甚至不需要通过总线,比如双核系统共享写回式 L2 cache
:em06: 如两核共享L2 cache,是无需写回内存。如果两核的L2 cache是分开的,则必写回内存
这个是intel前端总线架构决定的,参考资料我只知道mindshare那边讲P4前端总线的书,我的观点也是从那里得来的。
如果真有这种cache分开不同步回内存的CPU,希望能看到有关资料。
zx_wing 回复于:2008-03-04 21:59:48
引用:原帖由 yangtou 于 2008-3-4 21:54 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8038994&ptid=1059006]
cache一致不一定要写回内存,甚至不需要通过总线,比如双核系统共享写回式 L2 cache
哦,我没看到兄台说的是共享写回式 L2 cache 。
你说的是对的,这种情况不需要同步回内存,多谢指出。
safedead 回复于:2008-03-05 11:38:28
volatile明显是要和NUMA架构过不去:mrgreen: :mrgreen: :mrgreen: :mrgreen:
safeking 回复于:2008-03-05 13:54:40
强,zx_wing 分析的透彻
ujjidt 回复于:2008-03-05 15:45:19
好文章,做个记号!
whyglinux 回复于:2008-03-06 22:12:26
引用:原帖由 南海一钓叟 于 2008-2-29 13:44 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022088&ptid=1059006]
上次面试被考官问到volatie变量,他说这个要结合cache来讲才能说的清,那位大侠给讲一下。
这里说的“cache”不是通常意义上的缓存,即CPU内部缓存或磁盘缓存等。它是一种更加广义上的缓存概念:以提高访问效率为目的,把数据或处理后的数据保存起来加以利用的现象都可以称为缓存。
题目中的缓存实际上指的是寄存器,与CPU缓存无关。编译器的优化可以把本来在内存中进行访问的数据装入(缓存)到寄存器中以提高访问效率。然而这种优化在一些情况下可带来问题;此时,应该用 volatile 告诉编译器不要进行这种优化以避免问题的出现。
[ 本帖最后由 whyglinux 于 2008-3-6 23:02 编辑 ]
Godbach 回复于:2008-03-06 22:42:27
强啊,好好学习了:em17:
飞雪横天 回复于:2008-03-07 10:08:11
引用:原帖由 sirouni 于 2008-3-2 18:29 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8028944&ptid=1059006]
Do you volatile? should you?
by Dr. Kevin P. Dankwardt
Volatile is an ANSI C type modifier that is frequently needed in C code that is part of signal/interrupt handlers, threaded code, and ...
看了这篇文章, 我倒是对volatile的理解更加糊涂了.
不同线程中对同一个变量的访问也需要使用volatile么? 这样的话,对我的打击也太大了点.
我以前可是经常在一个线程通过设置一个全局变量通知另外一个线程退出的, 我可重来没有设置过volatile阿.
flw2 回复于:2008-03-07 10:17:28
引用:原帖由 飞雪横天 于 2008-3-7 10:08 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049579&ptid=1059006]
看了这篇文章, 我倒是对volatile的理解更加糊涂了.
不同线程中对同一个变量的访问也需要使用volatile么? 这样的话,对我的打击也太大了点.
我以前可是经常在一个线程通过设置一个全局变量通知另外一个 ...
看33楼
yangtou 回复于:2008-03-07 10:19:17
c++标准“貌似”没有线程的概念,volatile主要是对signal、setjmp/longjmp等
对与你这个全局变量我觉得应该使用明确的线程同步原语,用volatile可能不够
tgbvc 回复于:2008-03-07 10:32:07
引用:原帖由 飞雪横天 于 2008-3-7 10:08 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049579&ptid=1059006]
看了这篇文章, 我倒是对volatile的理解更加糊涂了.
不同线程中对同一个变量的访问也需要使用volatile么? 这样的话,对我的打击也太大了点.
我以前可是经常在一个线程通过设置一个全局变量通知另外一个线程退出的, 我可重来没有设置过volatile阿.
volatile只与编译器优化有关。但是,实际是否优化要看这段代码是否有优化空间,也就是有没有优化的必要 —— 当然,大多情况这决定于编译器智能程度,智能度高的编译器发现优化空间的能力就强,反之亦然。所以,你没有volatile修饰多线程共享的数据,程序运行也正常,说明编译器对与这个数据相关的代码没有优化。
flw2 回复于:2008-03-07 10:37:11
引用:原帖由 tgbvc 于 2008-3-7 10:32 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049768&ptid=1059006]
volatile只与编译器优化有关。但是,实际是否优化要看这段代码是否有优化空间,也就是有没有优化的必要 —— 当然,大多情况这决定于编译器智能程度,智能度高的编译器发现优化空间的能力就强,反之亦然。 ...
不对,很多时候不是没有优化,是不能优化
飞雪横天 回复于:2008-03-07 10:40:52
引用:原帖由 flw2 于 2008-3-7 10:17 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049650&ptid=1059006]
看33楼
发现一个小问题,和主题无关,曾经讨论过
3楼 do_someting(如果是函数)可能会修改全局的i,而编译器不可能去检查这个事实,所以
while(i)
do_something();
不是例子
while(i) {
/**/
}
发现一个小问题,和主题无关,曾经讨论过, 能够告诉我讨论的主题地址么?
还有这句话: 可能会修改全局的i,而编译器不可能去检查这个事实, ??????????
一个线程通知另外一个线程退出的模型就是这样的:
while(threadrunning == 0)
{
pthread_exit(NULL);
}
如果全局变量threadrunning被修改, 而编译器不去检查这个事实, 那么如果优化掉的话, 那不是天下大乱了?
飞雪横天 回复于:2008-03-07 10:43:02
引用:原帖由 tgbvc 于 2008-3-7 10:32 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049768&ptid=1059006]
volatile只与编译器优化有关。但是,实际是否优化要看这段代码是否有优化空间,也就是有没有优化的必要 —— 当然,大多情况这决定于编译器智能程度,智能度高的编译器发现优化空间的能力就强,反之亦然。 ...
您的意思是说, 有的编译器可能给优化掉了, 有的编译器就不优化了, 那就是说我们这种设计最好是加上volatile了?
我基本上在这种情况下,没有见过通知线程退出的变量,需要用volatile
flw2 回复于:2008-03-07 10:53:18
引用:原帖由 飞雪横天 于 2008-3-7 10:40 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049829&ptid=1059006]
发现一个小问题,和主题无关,曾经讨论过
3楼 do_someting(如果是函数)可能会修改全局的i,而编译器不可能去检查这个事实,所以
while(i)
do_something();
不是例子
while(i) {
/**/
}
发现 ...
不是你上面这个意思
while(threadrunning == 0)
{
pthread_exit(NULL);
}
编译器不能把threadrunning优化到寄存器,否则pthread_exit要是修改这个变量咋办?虽然pthread_exit不会修改你这个全局变量,但是编译器不知道这个事实,也没办法知道它是否修改
飞雪横天 回复于:2008-03-07 10:57:50
引用:原帖由 flw2 于 2008-3-7 10:37 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049807&ptid=1059006]
不对,很多时候不是没有优化,是不能优化
唉, 那多线程之间的共享变量到底如何处理?
以我的想法, 多线程下共享变量缓冲在cache当中, 由于所有线程共享内存,所以所有线程访问同一个变量时都是访问同一个cache,因此
应该不需要使用volatile, 因此在我的概念中,只有寄存器上的处理才需要用volatile, ?????????
flw2 回复于:2008-03-07 10:59:34
引用:原帖由 飞雪横天 于 2008-3-7 10:57 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049985&ptid=1059006]
唉, 那多线程之间的共享变量到底如何处理?
以我的想法, 多线程下共享变量缓冲在cache当中, 由于所有线程共享内存,所以所有线程访问同一个变量时都是访问同一个cache,因此
应该不需要使用volatile, ...
你是没看我的留言还是没看懂我在说什么?
飞雪横天 回复于:2008-03-07 11:02:37
引用:原帖由 flw2 于 2008-3-7 10:53 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049940&ptid=1059006]
不是你上面这个意思
while(threadrunning == 0)
{
pthread_exit(NULL);
}
编译器不能把threadrunning优化到寄存器,否则pthread_exit要是修改这个变量咋办?虽然pthread_exit不会修改你这个全 ...
可是下面这个程序, 您如何解释"while(total < 100) ", 莫非此时编译器就知道不会修改total,所以到cache取值?
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <sys/time.h>
#ifndef VOLATILE
#define VOLATILE
#endif
VOLATILE int total = 0; /* record the handle running times */
void handle(int signo)
{
if(signo == SIGALRM)
{
printf("Total = %d\n", total);
exit(0);
}
total++;
}
int main(int argc, char* argv[])
{
VOLATILE unsigned x = 0;
VOLATILE int i, j;
struct sched_param param;
struct itimerval val;
val.it_interval.tv_sec = 0;
val.it_interval.tv_usec = 10000; // 10 ms
val.it_value.tv_sec = 0;
val.it_value.tv_usec = 10000; // 10 ms
setitimer(ITIMER_VIRTUAL, &val, NULL);
param.sched_priority = 99;
if(sched_setscheduler(0, SCHED_RR, ¶m) == -1)
{
perror("setting priority");
exit(1);
}
#define BIGNUM (1000)
alarm(20);
signal(SIGALRM, handle);
signal(SIGVTALRM, handle);
while(total < 100)
{
for(i = 0; i < BIGNUM; i++)
{
for(j = 0; j < BIGNUM; j++)
x = x + j;
}
}
printf("x = %u\n", x); // so optimizers doesn't throw away the loop
printf("total = %d\n", total);
return 0;
}
飞雪横天 回复于:2008-03-07 11:03:54
引用:原帖由 flw2 于 2008-3-7 10:59 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8050002&ptid=1059006]
你是没看我的留言还是没看懂我在说什么?
呵呵, 这个是没看到,我写帖子的时候你帖子还没回呢
RobinsonNie 回复于:2008-03-07 11:06:26
引用:原帖由 飞雪横天 于 2008-3-7 10:08 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049579&ptid=1059006]
看了这篇文章, 我倒是对volatile的理解更加糊涂了.
不同线程中对同一个变量的访问也需要使用volatile么? 这样的话,对我的打击也太大了点.
我以前可是经常在一个线程通过设置一个全局变量通知另外一个 ...
看着都着急, 你这么想就明白了.
编译器在进行对源码编译过程中,通常会对反复使用的变量整个逼本到reg当中从而提高访问速度.由volatile声明的变量,实际上就是通知编译器该变量会以你意想不到的方式被更改,所以你丫别优化我. 那么编译器意想不到的方式有哪几种呢,如下:
1.Memory map的硬件寄存器(比如). 状态寄存器是最常见, 你想呀, 硬件修改这变量他会告诉编译器嘛, 不会也没这能力, 那编译器不就傻呵呵的把该变量优化了嘛, 一优化就坏事了, 因为我们读到的都是reg中存储的旧的状态寄存器的值, 而非memory当中最新的状态寄存器值. 抄个定义给你看看: #define rIICSTAT (*(volatile unsigned *)0x54000004) //IIC status
2.多线程中被几个线程共享的变量. 线程修改共享变量var会通知编译器嘛,不会也没这能力,所以线程A使劲读着var在reg中的副本(狗日的编译器优化),读出来1时他好大展鸿图呀,结果读出来的都是0, 而线程B早就把var变量给修改为1了,怪谁呀,只怪没加volatile,加上volatile不早就读memory当中的var新值1了嘛.
3.ISR当中用.这个麻烦,需要拿程序举例,俺就不写了.
再有, 讨论volatile就讨论volatile. 跟cache有嘛关系, cache是不可见的...不可见的...我看不着~~~
tgbvc 回复于:2008-03-07 11:17:09
引用:原帖由 飞雪横天 于 2008-3-7 10:43 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8049845&ptid=1059006]
您的意思是说, 有的编译器可能给优化掉了, 有的编译器就不优化了, 那就是说我们这种设计最好是加上volatile了?
我基本上在这种情况下,没有见过通知线程退出的变量,需要用volatile
是的。为了避免以后因为在不同编译器或在不同版本的同一编译器编译而带来的麻烦,建议应该这样做。除非你确定以后不换编译器编译这个程序。
yangtou 回复于:2008-03-07 11:49:27
这里有一个gcc-volatile的讨论http://gcc.gnu.org/ml/gcc/2007-10/msg00266.html
> If you really want all externally-visible accesses to v to be made exactly
> as the code directs, rather than allowing gcc to optimise them in any way that
> (from the program's POV) it's just the same 'as-if' they had been done
> exactly, make v volatile.
That is not enough. Apart from the lack of ISO semantics for volatile,
typically a compiler will take volatile as a hint to not hold
values of the variable in a register.
On a multi-processor, this is not enough, because each CPU
may still hold modified values in separate caches.
Perhaps gcc actually puts a RW barrier to force
cache synchronisation on every volatile access..
this seems rather expensive and very hard to do since
it is very dependent on the actual box (not just the
processor). Some processor caches might require external
electrical signals to synchronise, for example. This is
quite possible if you have multiple CPU boards in a box.
But I don't actually know what gcc does, although I guess
it does nothing. The OS has to do the right thing here
when a mutex is locked etc, but the code for that is
probably in the kernel which is better able to manage
things like cache synchronisation than a compiler.
c/c++没有多线程的概念,没有一个明确的memory model,volatile不能保证共享变量在多线称间同步,
应该使用明确的lock。
adonais 回复于:2008-03-09 15:41:40
引用:原帖由 zx_wing 于 2008-2-29 14:20 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022369&ptid=1059006]
他骗你的。
volatie变量和cache没有关系,只和内存有关系。
简单点说就是每次操作前从内存取值
有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ----> 操作 ---->写回内存
没 ...
留个位置,呵呵
duanius 回复于:2008-03-19 21:39:03
顶上来顶上来
blueboy83 回复于:2008-03-20 19:01:35
volatie变量和cache没有关系,只和内存有关系。
简单点说就是每次操作前从内存取值
有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ----> 操作 ---->写回内存
楼上的回答很清楚
cunettenuc 回复于:2008-03-20 20:05:07
引用:原帖由 zx_wing 于 2008-2-29 14:20 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8022369&ptid=1059006]
他骗你的。
volatie变量和cache没有关系,只和内存有关系。
简单点说就是每次操作前从内存取值
有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ----> 操作 ---->写回内存
没 ...
我有一个问题想请教, 就是在多线程中, 有一个全局变量没有用volatile去修饰, 而我只是用锁去进行互斥地访问.
那么假如我线程1在执行时将这个变量放到了cache中, 有没有这种可能: 也就是在放弃锁的时候这个变量仍然没有被写回内存,那么我另外一个线程从内存中读的话就不会读到最新的值了.
想请教一下会不会有这种可能. 谢谢.
zx_wing 回复于:2008-03-20 22:15:55
引用:原帖由 cunettenuc 于 2008-3-20 20:05 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8107111&ptid=1059006]
我有一个问题想请教, 就是在多线程中, 有一个全局变量没有用volatile去修饰, 而我只是用锁去进行互斥地访问.
那么假如我线程1在执行时将这个变量放到了cache中, 有没有这种可能: 也就是在放弃锁的时候这 ...
前面已经说了,如果读的线程和写的线程在一个CPU,那么它直接从cache里读到这个变量的值。
如果读的线程在另一个CPU,那么硬件保证了cache的一致性,见前面的帖子。
两种情况都保证读到的值是最新的
wxgwin 回复于:2008-04-03 09:43:47
我是服了
cu上就是高手如林
服:P
|