作者:曹忠明,华清远见嵌入式学院讲师。
volatile在词典中的意思是易变的,反复无常的。它在我们的程序设计中常常用到的。volatile是一个关键字,用来修饰一个变量,告诉编译器在编译的时候不要对其进行优化,在操作寄存器和多线程中这种用法是最常见的。
有这样一个例子:
#include <stdio.h>
#include <pthread.h>
void my_func();
int? i;
int main()
{
pthread_t my_thread;
int err,k;
if ((err = pthread_create(&my_thread,NULL,(void *)my_func,NULL)) < 0)
perror("can't create thread:%s\n");
i = 2;
while(i == 2);
printf("main:%d\n",i);
while(1);
return 0;?
}
void my_func()
{
sleep(1);
i = 3;
printf("my_func:%d\n",i);
}
这个例子本意是想让主程序进入while(i == 2)这个循环,直到线程中将这变量i的值修改后跳出循环,可是结果是
my_func:3
这与想像中的结果完全不一样,是什么原因造成这样的结果呢?查看一下汇编代码,才知道,是编译器将这段代码给优化掉了,汇编代码如下:
.file "test.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "my_func:%d\n"
.text
.p2align 4,,15
.globl my_func
.type my_func, @function
my_func:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl $1, (%esp)
call sleep
movl $3, 4(%esp)
movl $.LC0, (%esp)
movl $3, i
call printf
leave
ret
.size my_func, .-my_func
.section .rodata.str1.1
.LC1:
.string "can't create thread:%s\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
leal -8(%ebp), %eax
movl $0, 12(%esp)
movl $my_func, 8(%esp)
movl $0, 4(%esp)
movl %eax, (%esp)
call pthread_create
testl %eax, %eax
js .L9
.L4:
movl $2, i
.L6:
jmp .L6
.L9:
movl $.LC1, (%esp)
call perror
jmp .L4
.size main, .-main
.comm i,4,4
.ident "GCC: (GNU) 4.1.3 20080623 (prerelease) (Ubuntu 4.1.2-23ubuntu3)"
.section .note.GNU-stack,"",@progbits
在定义变量i的时候添加上volatile后:
int volatile i;
的结果为:
my_func:3
main:3
这个结果显然达到了我们预期的效果,再查看一下他的汇编代码,会看到那个带有条件的循环语句。
.file "test.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "my_func:%d\n"
.text
.p2align 4,,15
.globl my_func
.type my_func, @function
my_func:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl $1, (%esp)
call sleep
movl $3, i
movl i, %eax
movl $.LC0, (%esp)
movl %eax, 4(%esp)
call printf
leave
ret
.size my_func, .-my_func
.section .rodata.str1.1
.LC1:
.string "can't create thread:%s\n"
.LC2:
.string "main:%d\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
leal -8(%ebp), %eax
movl $0, 12(%esp)
movl $my_func, 8(%esp)
movl $0, 4(%esp)
movl %eax, (%esp)
call pthread_create
testl %eax, %eax
js .L13
.L4:
movl $2, i
.L6:
movl i, %eax
cmpl $2, %eax
je .L6
mov i, %eax
movl $.LC2, (%esp)
movl %eax, 4(%esp)
call printf
.L8:
jmp .L8
.L13:
movl $.LC1, (%esp)
call perror
.p2align 4,,3
jmp .L4
.size main, .-main
.comm i,4,4
.ident "GCC: (GNU) 4.1.3 20080623 (prerelease) (Ubuntu 4.1.2-23ubuntu3)"
.section .note.GNU-stack,"",@progbits
比较红色部分就会看到是什么造成这种差异了!
为什么加上volatile和不加就有这么大的差距的,原因是每次使用变量都去内存中取值,然后通过系统总线传到CPU处理,会增加很大的开销,所以在CPU的cache中位变量啊做了一个副本,通过这个副本来进行赋值。在程序中首先对i 进行复制“i = 2”,然后又将i和2进行比较“i == 2”编译器认为i的值是2,没有变化,认为这个比较没有意义就将其优化掉了,将程序陷入无条件的死循环中,在线程my_func中修改i 的值夜就没有意义了,最终结果就是我么看到了那样了,加上volatile编译器就不会优化了,每次都被迫去内存中去取值,达到了我们预期的结果。
文章评论(0条评论)
登录后参与讨论