Posts Tagged ‘volatile’

September 4, 2010

  正常情况下,在终端编译执行该多线程程序,首先打印n(0),5秒后再次打印n(1),然后该进程正常退出。
  但,如果使用gcc编译此程序时,使用优化选项(比如-O1),再执行此程序。首先打印n(0),5秒后再次打印n(1),然后呢,然后就死循环啦!
  为什么呢?看一看汇编代码就知道了,使用gcc -O1 -S命令编译第一段代码,得到的汇编码:

1
2
3
4
5
6
7
main:
    movl    n(%rip), %eax #从内存中取n值至寄存器eax
.L3:
    testl   %eax, %eax # 与测试eax
    je  .L3 # eax0时跳转至标号L3处
    movl    $0, %eax
    ret

  真相大白了吧,那该怎么办?对,volatile,参看这里

Tags: ,. 54 views
August 10, 2010

volatile

  可能很多人都没用过C/C++中的这个关键词,甚至不知道它的存在,本人以前也只是有所耳闻,但似懂非懂。
  这是一个类型修饰符,位置同conststatic等。一个使用volatile修饰的变量,比如volatile int i; 每次对该变量的直接引用,都会访问内存,而不是从寄存器中读取(如果其已经在寄存器中)。这样一来,volatile似乎没什么用处,反倒会使数据的读取相对变慢很多。但是,如果没有volatile,编译器可能会优化你的程序,使得数据从寄存器中读取,从而加快程序的运行,但如果这个变量是同其它进程/线程共享的,就可能造成数据的不一致。多线程情况下,你可以使用互斥机制来保证对共享数据访问的原子性。但是,在单片机等嵌入式环境中,硬件经常不会有这种互斥机制的支持,这时某些共享的数据(比如端口)就可能会产生不一致的情况。而使用volatile就会使编译器不对代码进行优化,每次对该变量的访问都会从内存中读取。
  下面通过观察使用volatile前后编译产生的汇编代码的不同,来加深对volatile关键词的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[ ... ]
main:
[ ... ]
	call	foo # 调用函数foo,返回值保存至寄存器eax
	movl	%eax, 44(%esp) # 为i赋值
	movl	44(%esp), %edx # 读取i值
	movl	44(%esp), %ecx # 读取i值
	movl	44(%esp), %eax # 读取i值
	movl	%ecx, 16(%esp) # 参数k入栈,使用i值
	movl	%edx, 12(%esp) # 参数j入栈,使用i值
	movl	%eax, 8(%esp) # 参数i入栈
	movl	$.LC0, 4(%esp) # 格式字符串地址入栈
	call	__printf_chk
	movl	$0, %eax
	leave
	ret
[ ... ]
Tags: ,. 1,268 views
Page 1 of 11