89467505 2020-01-05
我们先来看如下代码:
#include <stdio.h> #include <stdlib.h> #include <signal.h> int flag=1; void Handler(int signo){ printf("signo=%d\n",signo); flag=0; } int main(){ signal(2,Handler); while(flag){} }
在不优化的情况下直接进行编译,我们可预见:程序运行起来,当给这个进程发送二号信号flag值才会变为0使循环结束程序运行结束。
但当用O2使编译器对这个代码进行优化时,就会发现按下ctrl+c发送2号信号时,循环依旧不会停止。这是为什么呢?
原来:
在编译器在优化过程中,若编译器判定某个数据是一个比较高的开销,然后编译器没有检测到有代码修改这个数据,便会把频繁使用的数据放到了寄存器中(while循环频繁使用flag,Handle函数虽对他进行修改但是由内核调用的,编译器并不知道),编译器就可能作出了错误的判断,这时就直接把flag这个值优化到寄存器里了,Handle函数对flag的修改只是改变了内存中的flag并没有改变寄存器中的flag,因而while判断时用到寄存器中的flag一直是1.所以循环就结束不了。
为了避免这种编译器的错误决措,我们引入volatile关键字
这个关键字修饰变量就是告诉编译器,这个变量必须每次都从内存中读,不敢直接加载到寄存器中,即volatile的目的就是保持内存可见性
#include <stdio.h> #include <stdlib.h> #include <signal.h> volatile int flag=1; void Handler(int signo){ printf("signo=%d\n",signo); flag=0; } int main(){ signal(2,Handler); while(flag){} }
在flag前加上volatile,这时候不管怎么优化flag都是从内存中读取的,一改变他就可以读入新的值,因而这个程序当接收到2信号时就可以正常退出了。
volatile要经常使用在多线程中,因为编译器对于多执行流的情况不太会判断,所以volatile经常要使用在多线程来让cpu用的变量都是新的。
与volatile相对的是register,即告诉编译器把这个变量放到寄存器中。
但是这register个关键字不经常用了因为编译器知道什么变量该放什么不该放,会自动优化。