为什么是volatile

限于喜欢 提交于 2019-11-27 23:34:29

  在上篇博文《线程并发执行带来的问题》的评论中,有几个朋友(锦瑟无端五十弦Kevin-moon等)说到了要用volatile声明变量的问题,首先非常感谢他们的指点。以前一直只知道volatile关键字是通知编译器在对这个变量进行操作的时候,每次都从其内存中读取数据,不要对其进行优化,但对其具体的过程不了解。今天看一篇文章《汇编与C之间的关系》,恰好提到这个问题,于是就将其记录下来,还请您多多指教。

  首先,现看一个简单的程序,就是用tmp对buf数组进行赋值,程序很简单,所以就没加注释了:

 

 1 #include<stdio.h> 2  3 unsigned char tmp; 4  5 unsigned char buf[3]; 6  7  int main() 8 { 9         buf[0] = tmp;10         buf[1] = tmp;11         buf[2] = tmp;12 13         return 0;14 }15  

首先对其进行编译:

 

huangwei@ubuntu:~/Desktop$ gcc volatile.c -o a.out -g

huangwei@ubuntu:~/Desktop$ objdump -dS a.out

 

 1     buf[0] = tmp; 2   80483b7:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax 3  80483be:    a2 19 a0 04 08           mov    %al,0x804a019 4     buf[1] = tmp; 5   80483c3:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax 6  80483ca:    a2 1a a0 04 08           mov    %al,0x804a01a 7     buf[2] = tmp; 8   80483cf:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax 9  80483d6:    a2 1b a0 04 08           mov    %al,0x804a01b10  

上面生成的汇编代码我们可以看出,程序每次从0x804a018地址取出tmp的值,然后赋值给buf数组元素,这样的代码,显然符合我们的预期。接着,我们让编译器对其进行优化编译,我们再来看看其生成的汇编代码的区别:

 

huangwei@ubuntu:~/Desktop$ gcc volatile.c -o b.out -g -O

huangwei@ubuntu:~/Desktop$ objdump -dS b.out

 

1     buf[0] = tmp;2   80483b7:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax3  80483be:    a2 19 a0 04 08           mov    %al,0x804a0194     buf[1] = tmp;5   80483c3:    a2 1a a0 04 08           mov    %al,0x804a01a6     buf[2] = tmp;7   80483c8:    a2 1b a0 04 08           mov    %al,0x804a01b8  

跟上面的代码相比,可以发现,程序中只有第一次赋值的时候才从内存中取得tmp的值,而其他的时候,都是直接将寄存器al中保存的最开始的tmp的值赋给buf数组元素。这样在一般的情况下,是没有问题的,但是联系到线程并发执行的问题,我们再来仔细考虑一下这个问题。当该线程第一次从内存中取出tmp的值后,操作系统发生调度,调度另一个线程执行,而这个线程又对tmp的值进行了修改,接下来,当原始线程恢复执行后,其寄存器al中保存的仍然是tmp最初的值,其给tmp的赋值,仍是tmp最初的值。这样,就产生的数据不同步的问题。

 

  接下来,我们用volatile来声明tmp变量,其他的跟上面的程序一样:

 

  volatile unsigned char tmp;

 

我们再让编译器对其进行优化编译:

 

huangwei@ubuntu:~/Desktop$ gcc volatile.c -o c.out -g -O

huangwei@ubuntu:~/Desktop$ objdump -dS c.out

 

 1     buf[0] = tmp; 2   80483b7:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax 3  80483be:    a2 19 a0 04 08           mov    %al,0x804a019 4     buf[1] = tmp; 5   80483c3:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax 6  80483ca:    a2 1a a0 04 08           mov    %al,0x804a01a 7     buf[2] = tmp; 8   80483cf:    0f b6 05 18 a0 04 08     movzbl 0x804a018,%eax 9  80483d6:    a2 1b a0 04 08           mov    %al,0x804a01b10  

 

  从上面的汇编代码可以看出,现在程序每次都从内存中取出tmp的值,然后赋值给buf数组元素,这样的话,就符合预想的情况了。上面只是个人的一些想法,错误的地方,还请您斧正。谢谢啦。

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!