浅析volatile修饰符

目录

前言

一、下面代码逻辑上是否有问题?

二、认识volatile

总结


 

前言

        最近在工作中遇到了一个问题,定位后发现是由于编译器优化了一个全局变量,导致依赖于该全局变量的判断逻辑未发生。下来我们上代码一起来看一下。


提示:以下内容基于C语言讲解。

一、下面代码逻辑上是否有问题?

#include <stdio.h>
#include <pthread.h>
...

bool runFlag = TRUE;
bool exitFlag = FALSE;

void threadFunc(void *arg)
{
    while(runFlag)
    {
        ...
    }

    exitFlag = TRUE;
}

void exitThread(void)
{
    runFlag = FALSE;

    for(; ;)
    {
        if (TRUE == exitFlag)
        {
            ...
        }
    }
}

void main(void)
{
    ...

    pthread_create(&tid, &attr, threadFunc, NULL);
    sleep(100);
    exitThread();
}

二、认识volatile

 volatile是一个特征修饰符。它的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,并且要求每次直接读值。

什么叫直接读值呢?我们可以通过反汇编来看看,上述代码被编译器编译之后是什么样子的。

*****************************************************************************/
void exitThread(void)
{
   0:   e92d47f0        push    {r4, r5, r6, r7, r8, r9, sl, lr}
   4:   e1a04000        mov     r4, r0

    runFlag = FALSE;
   8:   e3a00000        mov     r0, #0
   c:   e1a05104        lsl     r5, r4, #2
  10:   e59f3120        ldr     r3, [pc, #288]  ; 
  14:   e0852004        add     r2, r5, r4
  18:   e08f3003        add     r3, pc, r3
  1c:   e0822282        add     r2, r2, r2, lsl #5
  20:   e59f1114        ldr     r1, [pc, #276]  ; 
  24:   e0833102        add     r3, r3, r2, lsl #2
  28:   e08f1001        add     r1, pc, r1
  2c:   e5933014        ldr     r3, [r3, #20]
  30:   e7810104        str     r0, [r1, r4, lsl #2]
  for (; ;)
    {
        if (TRUE == exitFlag)
  34:   e3530001        cmp     r3, #1
  38:   1afffffd        bne     34 
        {
......

通过arm-linux-objdump -D -S *.o命令(目标文件为上述代码编译产生的目标文件,objdump需要使用目标机器工具链)来反汇编,同时对照C语言,我们可以发现,编译之后的代码,在函数进来之后,将exitFlag的值取出来放在了r3寄存器中,然后进入for循环后一直使用的是寄存器r3中的值,这就导致,当内存中exitFlag的值被更改后不会同步到exitThread函数中,从而产生与预期不一样的结果。

所以,上面说的每次读值的意思是:告诉编译器这个值可能随时会被改变,在用到这个变量时必须每次都从内存中重新读取这个变量的值,而不是使用保存在寄存器中的备份。

那么编译器为什么会做这个优化呢?原来,编译器在编译的时候,会根据优化级别来优化我们的代码,为了提高访问速度,编译器会有选择的将访问变量的值先读取到寄存器中,待执行完一定的步骤后再写回内存或重新从内存中读取到寄存器。那么在这个过程中,可能就会带来灾难性的后果。


总结

1、遇到多线程共享的变量时,可以用volatile关键字告诉编译器不要进行优化。

2、映射在存储器中的硬件寄存器,如状态寄存器需用volatile来修饰,因其每次都有不同的含义。

3、在中断服务程序中修改的供其它程序检测的变量需要加volatile;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值