Linux中spinlock--来自深入理解Linux内核一书

自旋锁(spin_lock)

自旋锁在发现锁被占用时,将陷入忙等状态,反复执行紧凑循环指令,直到锁被释放。
自旋锁的循环指令表示“忙等”。即使等待过程中内核控制路径无事可做(除了浪费时间),也占用CPU,保持在CPU上运行。

在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;}

自旋锁相关函数或宏定义:

spin_lock_init() 把自旋锁设置为1;

spin_lock 循环直到自旋锁变为1,然后把自旋锁置为0;

spin_unlock,把自旋锁置为1;

spin_unlock_wait等待,直到自旋锁变为1;

spin_is_locked

spin_trylock把自旋锁置为0,若原来是1,则返回1,否则返回0;

_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;  <-- need atomic operation
 *   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;  <-- need atomic operation
 *   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; <--+ need atomic operation
	 *   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宏:

1、调用preempt_disable以禁用内核抢占;

2、调用_raw_spin_trylock,对自旋锁的slock字段执行原子性的测试和设置操作;

3、如果自旋锁中的旧值是正数,宏结束:内核控制路径已获得自旋锁;

4、否则,内核控制路径无法获得自旋锁,因此宏必须执行循环一直到其他CPU释放自旋锁。调用preempt_enable开启内核抢占。如果在执行spin_lock之前内核抢占被启用,那么其他进程此时可以取代等待自旋锁的进程;

5、执行循环等待:

while(spin_is_locked(slp) && slp->break_lock)
    cpu_relax;//==》pause(rep, nop),可以减少能源消耗,延迟较短,
    加快了紧跟在后面的代码的执行,并减少能源消耗;优化自旋锁执行效率

6、跳到第一步,再次试图获取自旋锁;

非抢占式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;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值