早期的ARM指令集(V6前)提供SWP指令,该指令可原子交换寄存器和内存数据,用于实现信号量操作。
如下面这个例子:
sem_wati: MOV R1,#0 LDR R0,=SEM SWP R1,R1,[R0] ;取出信号量,并设置其为0 CMP R1,#0 ;判断是否有信号 BEQ sem_wait ;若没有信号,则等待
SWP指令的缺点是会lock总线,影响系统性能。
新的ARM指令(V6、V7)采用LDREX和STREX指令替换了SWP指令,可以实现对共享内存的非阻塞同步。
LDREX <Rt>,[Rn] STREX <Rd>,<Rt>,[Rn] ;STREX成功,Rd置0
新指令的例子: lock_mutex LDREX r1,[r0] ; 检查是否lock CMP r1,#LOCK ; 和LOCK比较,LOCK是0 BEQ lock_mutex ; 相等说明被锁定,自旋
MOV r1,#LOCK ; 不相等,加锁 STREX r2,r1,[r0] ; 尝试将r1写入锁 CMP r2,#0x0 ; 判断是否加锁成功(可能出现竞争导致加锁失败) BNE lock_mutex ; 如果不成功,从头判断 DMB ; 内存屏障保证前面操作成功 BX lr ; 返回
ulock_mutex DMB ; 内存屏障,保证安全访问 MOV r1,#UNLOCKED ; 解锁 STR r1,[r0] ; BX lr ;
LDREX和STREX是通过ARM内核的一个叫Exclusive Monitor的机制实现的,EM是一个状态机。 LDREX指令将Monitor置为Exclusive状态,STREX指令将Exclusive状态置回为Open状态,由此保证访问的唯一性。 但是在进程切换时,可能导致EM被打乱,因此需要执行CLREX指令,清除Exclusive Monitor。
|