嵌入式C语言关键字volatile以及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变量的读写,每次都要老老实实地从内存取,并且修改后,还要马上更新到内存:

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属性。


 

你可能感兴趣的:(嵌入式C语言关键字volatile以及cache对数据一致性的影响)