自旋锁(spin_lock)
自旋锁在发现锁被占用时,将陷入忙等状态,反复执行紧凑循环指令,直到锁被释放。
自旋锁的循环指令表示“忙等”。即便等待过程当中内核控制路径无事可作(除了浪费时间),也占用CPU,保持在CPU上运行。web
在Linux中,每一个自旋锁都用一个spinlock_t表示,其中包含两个字段:缓存
struct {
raw_spinlock_t raw_lock;//表示状态,1表示未加锁,任何负数和0表示加锁状态;
unsigned int break_lock;//仅在内核支持SMP和内核抢占的时候支持;
} spinlock_t;
raw_spinlock_t {volatile unsigned int lock;}
自旋锁相关函数或宏定义:svg
spin_lock_init() 把自旋锁设置为1;函数
spin_lock 循环直到自旋锁变为1,而后把自旋锁置为0;测试
spin_unlock,把自旋锁置为1;优化
spin_unlock_wait等待,直到自旋锁变为1;atom
spin_is_lockedcode
spin_trylock把自旋锁置为0,若原来是1,则返回1,不然返回0;xml
_spin_lock函数实现:进程
void __lockfunc _spin_lock(spinlock_t *lock) {
__spin_lock(lock);
}
__lockfunc = __attribute__ ((section(".spinlock.text")))
实际执行lock操做源码:
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
unsigned long tmp0, tmp1;
/*
* lock->slock : =1 : unlock
* : <=0 : lock
*
* for ( ; ; ) {
* lock->slock -= 1;
* if (lock->slock == 0) break;
* for ( ; lock->slock <= 0 ; );
* }
*/
__asm__ __volatile__ (
"# __raw_spin_lock \n\t"
".fillinsn \n"
"1: \n\t"
"mvfc %1, psw; \n\t"
"clrpsw #0x40 -> nop; \n\t"
DCACHE_CLEAR("%0", "r6", "%2")
"lock %0, @%2; \n\t"
"addi %0, #-1; \n\t"
"unlock %0, @%2; \n\t"
"mvtc %1, psw; \n\t"
"bltz %0, 2f; \n\t" //跳转
LOCK_SECTION_START(".balign 4 \n\t")
".fillinsn \n"
"2: \n\t"
"ld %0, @%2; \n\t"
"bgtz %0, 1b; \n\t" //跳转
"bra 2b; \n\t" //跳转
LOCK_SECTION_END
: "=&r" (tmp0), "=&r" (tmp1)
: "r" (&lock->slock)
: "memory" //告诉编译器,此段代码设定优化屏障;
#ifdef CONFIG_CHIP_M32700_TS1
, "r6"
#endif /* CONFIG_CHIP_M32700_TS1 */
);
}
这里注意到注释代码,实际为lock过程;
* for ( ; ; ) {
* lock->slock -= 1;
* if (lock->slock == 0) break;
* for ( ; lock->slock <= 0 ; );
* }
*/
如何控制lock->slock不会始终减一?
这里经过__raw_spin_trylock来进行加锁预断定,以下:
/**
* __raw_spin_trylock - Try spin lock and return a result
* @lock: Pointer to the lock variable
*
* __raw_spin_trylock() tries to get the lock and returns a result.
* On the m32r, the result value is 1 (= Success) or 0 (= Failure).
*/
static inline int __raw_spin_trylock(raw_spinlock_t *lock)
{
int oldval;
unsigned long tmp1, tmp2;
/*
* lock->slock : =1 : unlock
* : <=0 : lock
* {
* oldval = lock->slock;
* lock->slock = 0;
* }
*/
__asm__ __volatile__ (
"# __raw_spin_trylock\n\t"
"ldi%1, #0;\n\t"
"mvfc%2, psw;\n\t"
"clrpsw#0x40 -> nop;\n\t"
DCACHE_CLEAR("%0", "r6", "%3")
"lock%0, @%3;\n\t"
"unlock%1, @%3;\n\t"
"mvtc%2, psw;\n\t"
: "=&r" (oldval), "=&r" (tmp1), "=&r" (tmp2)
: "r" (&lock->slock)
: "memory"
#ifdef CONFIG_CHIP_M32700_TS1
, "r6"
#endif/* CONFIG_CHIP_M32700_TS1 */
);
return (oldval > 0);
}
解锁
static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
mb();//asm volatile("mfence":::"memory")或者asm volatile("lock; addl $0, 0(%esp)")
lock->slock = 1;
}
如上源码可解释为以下:
具备内核抢占的spin_lock宏:
一、调用preempt_disable以禁用内核抢占;
二、调用_raw_spin_trylock,对自旋锁的slock字段执行原子性的测试和设置操做;
三、若是自旋锁中的旧值是正数,宏结束:内核控制路径已得到自旋锁;
四、不然,内核控制路径没法得到自旋锁,所以宏必须执行循环一直到其余CPU释放自旋锁。调用preempt_enable开启内核抢占。若是在执行spin_lock以前内核抢占被启用,那么其余进程此时能够取代等待自旋锁的进程;
五、执行循环等待:
while(spin_is_locked(slp) && slp->break_lock)
cpu_relax;//==》pause(rep, nop),能够减小能源消耗,延迟较短,
加快了紧跟在后面的代码的执行,并减小能源消耗;优化自旋锁执行效率
六、跳到第一步,再次试图获取自旋锁;
非抢占式spin_lock宏:(实际状况:标记2处代码包含在另外的段中,以便在大多数状况下不要用不执行的代码填充硬件高速缓存)
1:lock; decb slp->slock;// 前缀lock;decb原子减一
jns 3f//检测符号标志,若是被清零,则调到标记3出继续执行;f表示向前;
pause
2: compb $0, slp->slock//不然在标记2处执行紧凑循环,直到自旋锁出现正值。而后从标签1处从新开始执行(b表示向后)
jle 2b
jmp 1b
3: …
spin_unlock宏:
movb $1, slp->slock;//如今的80x86微处理器老是原子的执行内存中的只写访问,因此不用lock前缀;
并在随后调用preempt_enable;