存在两段代码同时在多核上执行的情况,这时候才需要一个真正的锁来宣告代码对资源的占有。

几个核可能会同时access临界区,这时的spinlock是如何实现的呢?

要用到CPU提供的一些特殊指令,对lock变量进行原子操作。

SMP中spin_lock的实现

实现在include/linux/spinlock_api_smp.h

static inline void __raw_spin_lock(raw_spinlock_t*lock)

{

preempt_disable();

spin_acquire(&lock->dep_map, 0, 0,_RET_IP_);

LOCK_CONTENDED(lock, do_raw_spin_trylock,do_raw_spin_lock);}

SMP上的实现被分解为三句话。

Preempt_disable() 关抢占

Spin_acquire()是sparse检查需要

LOCK_CONTENDED()是一个宏,如果不考虑CONFIG_LOCK_STAT(该宏是为了统计lock的操作),则:

#define LOCK_CONTENDED(_lock, try, lock) \   lock(_lock)

则第三句话等同于:

do_raw_spin_lock(lock)

而do_raw_spin_lock()则可以从spinlock.h中找到痕迹:

static inline intdo_raw_spin_trylock(raw_spinlock_t *lock){    return arch_spin_trylock(&(lock)->raw_lock);}

看到arch,我们明白这个函数是体系相关的。下面分别分析ARM和x86体现结构下该函数的实现。

ARM中spin_lock的实现

static inline voidarch_spin_lock(arch_spinlock_t *lock)

{

unsigned long tmp;

__asm__ __volatile__("

1: ldrex %0, [%1]\n"

@将&lock->lock地址中的值,即lock->lock加载到tmp中,并设置&lock->lock为独占访问"
teq %0, #0\n"

@测试tmp是否为0

WFE("ne")

@不为0,则执行WFE指令。不为0,代表锁已被锁定,则通过WFE指令进入suspendmode(clock停止),直到该锁被释放时发出的SEV指令,CPU才会跳出suspend mode"

strexeq %0, %2, [%1]\n"

@将lock->lock加1,并解除lock->lock的锁定状态,tmp中存入返回状态"

teqeq %0, #0\n"

@如果执行成功,则tmp为0,成功获得所"

bne 1b"

@如果执行不成功,则tmp不为0,跳转到标号1处,继续获得锁。

: "=&r" (tmp)

: "r" (&lock->lock),"r" (1) : "cc")

smp_mb(); }

代码是一段内联汇编。Tmp为输出,放在寄存器中,在代码中以%0表示,&lock->lock为输入参数1,放在寄存器中,在代码中以%1表示,常数

1为输入参数2,放在寄存器中,在代码中以2%表示。

代码中,ldrex/strex以及WFE指令是关键。因lock->lock放在内存中,那么将lock->lock加1这一操作会经过读取内存,+1,写内存的操作,这一过程如果不是原子操作,那么其他核有可能在这一过程中访问lock->lock,造成错误。Ldrex/strex是ARM在arm v6中新增的指令,用于对内存区域的独占访问,WFE指令则可以在空等时间内暂停CPU的时钟,以达到省电的目的。

原文链接:http://blog.csdn.net/av_geek/article/details/41366539

05-11 20:35