本文以stm32为硬件平台,浅谈一下变量访问的互斥。
假设是裸板,主程序和中断服务程序都对某个变量进行修改,那么很可能造成数据的不一致。查了一下,主要有两种解决方法:第一种是关中断,在访问公共资源的时候,先把中断关掉,等访问完毕,再打开中断。这样就避免了中断程序和主程序的竞态。第二种是自己做一个互斥锁。
怎么做互斥锁呢?其实互斥是要依赖与硬件的,如果硬件没有相关的指令或者机制,那就不好办了。
在x86上,可以用汇编指令XCHG做一个互斥锁,但是stm32上就没有这个指令。在翻阅了《Cortex-M3权威指南》后,我有个发现。下面总结一下:
互斥访问分为加载和存储,对应的指令对是LDREX/STREX,LDREXH/STREXH,LDREXB/STREXB,分别对应字,半字,字节。
LDREX/STREX的语法格式为:
LDREX Rxf, [Rn, #offset] ; 把 (Rn+offset)地址处的内容加载到寄存器 Rxf里
STREX Rd, Rxf, [Rn, #offset] ;把Rxf的值存储到(Rn+offset)地址处,这个操作是可以被驳回的(也就是不被执行),如果驳回,Rd的值为1,如果操作成功,Rd的值为0.
在什么情况下会被驳回呢?在执行STREX的时候,仅当在这之前执行过LDREX指令,且在执行过LDREX指令之后没有其他的STR/STREX指令执行过的时候,才允许执行这个指令。也就是说只有在LDREX执行后的第一条STREX才能成功执行。
这个道理是易于理解的,在读,改,写的过程中,我们希望读的数据是最新的,也就是读过之后,这个数据不能被别人更新,只能被我更新。如果操作被驳回,说明别人已经修改过了,所以我读出的数据不是最新的,已经“脏了”。
关于例子文中有,这里就不赘述了。
用这对指令,就可以做一个互斥锁。怎么做呢?还是以《Cortex-M3权威指南》中的一个例子来说明。
记DeviceALocked是一个位于内存中的R/W变量,用于指示设备A是否已经在使用中。任何一个任务,若欲使用设备A,都必须先检查这个变量的值。如果它的值为零,则表示设备可以使用。在任务获取到设备A后,它要把DeviceALocked的值改为1,表示设备A已经被占用。在设备A使用完毕后,该任务通过重新清零DeviceALocked来释放设备A,从而使其它任务可以使用此设备。
TryToLockDeviceA
LDR R1, =DeviceALocked
LDREX R2, [R1]
CMP R2, #0 ;检查是否已被锁住
BNE LockDeviceAFailed
DeviceAIsNotLocked
MOV R0, #1 ;准备锁住设备A
STREX R2, R0, [R1] ;互斥写
CMP R2, #0
BNE LockDeviceAFailed ;STREX失败,设备A可能已被锁; 准备返回成功值
LockDeviceASucceed
MOV R0, #0
POP {R1, R2, PC} ;子程序返回
LockDeviceAFailed
MOV R0, #1
POP {R1, R2, PC} ;子程序返回