linux 内核 spin_lock,Linux中spinlock--来自深刻理解Linux内核一书

自旋锁(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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值