嵌入式C语言关键字volatile以及cache对数据一致性的影响

转自:http://m.blog.csdn.net/blog/a747lulu747/12423031 cache部分加入自己的一些理解

1、数据一致性是一个重要的问题,它定义了不同的CPU、系统总线所有的master看到的是相同的一片内存。

 

2、因为cache的存在,以及编译器对某些C语言语句的优化,使得CPU对某个内存变量的修改不能立刻更新到内存,或者其他系统的master修改了内存变量,但是CPU仍然使用cache中的值或者寄存器中的值来代表变量,此时就发生了数据一致性的问题:不同的系统总线master对同一个变量看到不同的值(CPU也可以看做是系统总线的master)。


3、先看看编译器优化对数据一致性的影响:

有如下语句:

int i = 0; 

while(1)

{

    i++;

    if(i > DELAY)

      break;

}

 

假设上述代码片段用来实现延时,或者其他功能。此时编译器会将变量i的值读入CPU内部寄存器,初始化为0,在while循环体中,对i++的操作就是对寄存器的操作:

	MOV      R7,#+0
	LDR      R6,=0xFF
	B        ??main_0
??main_0:
	ADD      R7,R7,#+1
	CMP      R7,R6
	BLT      ??main_1

以上是ARM中对应的汇编语言。可以看到编译器使用R7来保存i,R6来保存DELAY(值为0xFF)常量,然后在while循环中,只是对存i变量的寄存器R7加1,并没有对i变量的内存操作。如果其他CPU,或者总线master依赖于变量i来控制一些功能,此时就会出错,因为i的最新值只是存在于CPU寄存器中。

 

针对这样的情况,我们可以使用volatile关键字告诉编译器,对i变量的读写,每次都要老老实实地从内存取,并且修改后,还要马上更新到内存:

</pre><p style="padding-top: 0px; padding-bottom: 0px; margin-top: 10px; margin-bottom: 10px; font-family: Arial; font-size: 16px; line-height: 24px; -webkit-text-size-adjust: none; background-color: rgb(237, 237, 237);"></p><pre code_snippet_id="115567" snippet_file_name="blog_20131217_3_3245740" class="cpp" name="code" style="border: 1px solid rgb(255, 255, 204); background-color: rgb(255, 255, 252); font-family: 'Courier New'; overflow: auto; font-size: 16px; line-height: 24px; -webkit-text-size-adjust: none;">volatile int i = 0; 

while(1)

{

    i++;

    if(i > DELAY)

      break;

}

对应的汇编语言是:

	MOV      R1,#+0
	STR      R1,[SP, #+0]
	LDR      R6,=0xFF
	B        ??main_0
??main_0:
	LDR      R0,[SP, #+0]
	ADD      R0,R0,#+1
	STR      R0,[SP, #+0]
	LDR      R0,[SP, #+0]
	CMP      R0,R6
	BLT      ??main_1

上面的汇编语言中,R6是常量DELAY的值0xFF,而SP是变量i的内存地址。由此可见,每次都是先SP指向的内存中(即i的地址)LDR到R0,R0++,然后再将R0的值更新到SP指向的内存中(即i的内存位置)。判断i是否大于DELAY时,也是先将i的值从SP指向的地址中LDR到R0,然后在和R6(DELAY的值)比较大小。

 

4、再看看cache对数据一致性的影响

如果开启了数据cache,那么CPU对内存的读写都要经过cache缓冲。读就是读cache,写也是写cache。

考虑一下情况:CPUwhile循环退出依赖于一个内存地址的值,并且这个内存地址的值由另外一个外设负责更新。如果开启数据cache,那么CPU总是从cache中读取数据,这时,cache中的数据和内存中的数据出现不一致,程序执行出现逻辑错误。注意,此时CPU是使用LDR访存指令来访问内存,但是仍然没有得到正确的内存数据。即使使用volatile关键字也无济于事,因为volatile是在指令级上影响C语言到汇编语言的关键字,但是CPU在访问内存时,仍然需要经过cache的缓冲。

在开数据cache的情况下,可以将特定的内存地址设置为不使用cache,以确保CPU访问的是内存。具体就是页表项的Cache属性。

个人理解:

cache是夹在CPU和内存之间的一个缓存,当cache开启时,CPU从内存读写数据时会经过cache,并且留下备份,当涉及到有DMA等外设也会从内存读取数据时,容易产生错误,这时候需要手动去invalidate或者flush缓存(cache)。

常见的有三种情况:

第一种:写操作

当我们配置某些参数或者数据需要最终写到外设上时,CPU会先把他们写到cache上,假如有时序要求,cache没有马上刷新,这时候就存在你的内存和cache之间数据不一致的情况,这时候DMA去内存就拿到了错误的数据,正确的做法是在设置参数或者写入数据时,手动的flush一下cache,可以是全部或者几行,具体视情况而定,保证内存和cache之间数据的一致性。

第二种:读操作

当外设写某些数据到我们的内存上,我们需要读入时,数据也会经过cache,尤其是对相同寄存器的多次配置,或有值的变化时,很容易产生cache和内存数据不一致的情况,正确的方法是在读数据之前invalidate cache然后进行读操作,这时候数据不经过cache,保证是正确的数据。

第三种:写/读操作

当有读写混合的操作时,应该invalidate和flush联合起来使用,具体的方式需要视程序的情况而定,cache的一致性问题有时候很隐蔽,通过常规的gdb或者Jlink都是看到的经过cache的值(cache打开的情况下),不容易debug问题所在。无视程序性能的情况下,可以考虑关闭cache


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值