之前,提到了为什么要引入exclusive操作。ARM对于exclusive操作,新增了exclusive指令。
下面以一个例子,来说明下,这个是如何工作的。
以以下代码进行说明,标准的抢锁代码:
; void lock(lock_t *ptr) lock: ; is it locked? LDXR W1, [X0] ; Load current value of lock CMP W1, #LOCKED ; Compare with "LOCKED" B.EQ lock ; If LOCKED, try again ; Attempt to lock MOV W1, #LOCKDED STXR W2, W1, [X0] ; Attempt to lock CBNZ W2, lock ; If STXR failed, try again DMB SY ; Ensures acesses to the resource are not made ; before the lock is acquired RET
有两个线程,thread0和thread1(并行执行的两个线程,执行在2个cpu上),均执行该代码,进行抢锁操作。锁的地址,为0x0008_0020。在最开始的时候,锁没有被上锁,两个线程均可以抢。对于两个线程的exclusive monitor(以下简称为monitor),都是open状态。
线程0执行LDXR指令,exclusive的load操作,读取锁状态。此时线程0的monitor状态变为exclusive状态。
线程1执行LDXR指令,exclusive的load操作,读取锁状态。此时线程1的monitor状态变为exclusive状态。
当线程0执行STXR指令,exclusive的store操作,此时monitor状态是exclusive状态,因此store可以成功,W2的值被更新为0。
当线程0的monitor状态从exclusive状态切换到open状态时,硬件会自动将线程1的monitor状态从exclusive状态切换到open状态。因为两个cpu的monitor,检测的地址,是一样的。这种情况下,一个cpu的monitor状态从exclusive状态切换到open状态,硬件就会自动将另一个cpu的monitor状态从exclusive状态切换到open状态。
W2值被更新成0,表示线程1获取到锁。
当线程1执行STXR指令,exclusive的store操作,此时monitor状态是open状态,因此store不成,W2的值被更新为1。
W2值被更新成1,表示线程1获取锁失败,因此需要重新获取锁。
线程1没有获取到锁,返回执行lock函数。再执行LDXR指令,获取锁状态。
线程1执行完LDXR指令后,线程1的monitor状态,切换到exclusive状态。不过此时读取的W1的值为1,表示锁被别人上锁。
因此W1的值为1,因此B.EQ成立,线程1,重新执行lock函数。但是monitor的状态,依然是exclusive状态。
后面不管线程1,执行多少次LDXR,monitor的状态依然是exclusive状态,因此monitor从exclusive切换到open状态,是通过exclusive store指令或者一些其他事件,来进行切换的。
以上是2个线程通过exclusive指令,抢锁的过程,扩展到多个线程,多个cpu,原理也是一样的。最终,只会有一个线程,抢到锁,其他的线程均抢不到,并且monitor的状态为exclusive状态。
所以,在多核的系统中,抢锁的这种操作,软件是要使用exclusive操作,来抢锁的。这也是为什么,在多核的系统中,硬件是需要实现exclusive操作的。