假设我正在为 CPU 编写(汇编)代码,其唯一的原子操作是无条件交换——没有 LL/SC,没有比较和交换,只是简单的交换。(ARM9 就是这种野兽的一个例子。)有没有办法使用交换操作来执行原子递增/递减操作?
有一个相对简单的答案,即使用交换来构建自旋锁,并使用它来提供围绕普通增量和减量的关键部分。但这似乎很笨拙,而且我知道如果 CAS 或 LL/SC 可用,它可以在没有锁的情况下完成。所以我真正想知道的是是否有办法不使用锁来做到这一点。
假设我正在为 CPU 编写(汇编)代码,其唯一的原子操作是无条件交换——没有 LL/SC,没有比较和交换,只是简单的交换。(ARM9 就是这种野兽的一个例子。)有没有办法使用交换操作来执行原子递增/递减操作?
有一个相对简单的答案,即使用交换来构建自旋锁,并使用它来提供围绕普通增量和减量的关键部分。但这似乎很笨拙,而且我知道如果 CAS 或 LL/SC 可用,它可以在没有锁的情况下完成。所以我真正想知道的是是否有办法不使用锁来做到这一点。
我想不出另一种方法来做到这一点,仅仅是因为你需要交换和比较来检测你是否被允许继续。如果您没有比较和交换命令,则必须使用循环交换和比较来实现它,例如:
; Emulate atomic add/sub with atomic swap.
; On entry:
; r0 contains address of variable
; r1 contains value to add or subtract.
mutex: defw 0 ; mutual exclusion semaphore (0=free, 1=busy).
chng: push r2 ; save variables.
ld r2,1 ; claiming value.
spin: swap r2,(mutex) ; atomic swap (sounds like a good name for a band).
bnz spin ; loop until you have it.
add (r0),r1 ; emulated atomic change.
swap r2,(mutex) ; free mutex for everyone else.
pop r2 ; restore registers.
ret
如果您在代码中的很多地方都这样做,那真的很笨拙。我经常发现将'klunky'代码隔离到一个函数(如上)使它变得不那么klunky,因为你最终会得到很多看起来更简单的代码段:
myvar: defw 0
: : : : :
ld r0,myvar
ld r1,1 ; increment
call chng
或者,如果您希望代码更简单,请提供单独的incr
和decr
函数:
; Emulate atomic incr/decr with emulated atomic change.
; On entry:
; r0 contains address of variable
incr: push r1 ; save registers.
ld r1,1 ; increment.
call chng ; do it.
pop r1 ; restore registers.
ret
decr: push r1 ; save registers.
ld r1,-1 ; decrement.
call chng ; do it.
pop r1 ; restore registers.
ret
然后您的代码序列变为:
ld r0,myvar
call incr
或者,如果你可以做宏,一个更简单的:
atincr: defm ; do this once to define macro
ld r0,&1
call incr
endm
atincr myvar ; do this in your code, as much as you like.
如果你的 CPU 是单核的,那么你可以使用这种方式,当然不需要交换,但你必须小心。这是一个简单的案例:
//incrementing of variable
cli //disable interrupts if we aren't on high priviliegied code execution level
//perform non atomic increment of variable
sti //enable interrupts if we aren't on high priviliegied code execution level
//reading variable
cli //disable interrupts if we aren't on high priviliegied code execution level
//perform non atomic read operation
sti //enable interrupts if we aren't on high priviliegied code execution level
这种方法就像一把锁,但更加智能和快速。这种方法的唯一弱点是可能的 CPU 中断延迟,但是如果您的代码在禁用中断中短而快,那么这个延迟通常并不重要。