操作系统概念-内核同步-自旋锁

有了原子操作,就可以了制作控制临界区的锁机制了。自旋锁就是其中的一个代表。

自旋锁机制可以用门和锁的例子来比喻。进程执行到某个临界区,相当于要进入一栋房子,这是进程会检查屋内是否有人(进程),如果屋内没有人,则直接拿起钥匙进入并把门锁上(进入临界区);如果屋内有人(进程),则在门口等待(忙等待)屋内的进程出来再进去。可以看出,自旋锁最多只能被一个进程持有,如果有新的进程希望获取自旋锁,它将会一直忙等待直到前一个持有自旋锁的进程释放锁。

linux下的实现

自旋锁的结构定义在<linux/Spinlock_types.h>内:

typedefstructraw_spinlock {
	arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
	unsignedint break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
	unsignedint magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;
typedefstructspinlock {
	union {
		structraw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;

spinlock的调用接口定义在<linux/spinlock.h>中,加锁自旋锁有四个函数(或宏)

自旋锁接口

说明

void spin_lock(spinlock_t *lock)

获取指定的锁

void spin_lock_bh(spinlock_t *lock)

获取指定锁并禁止下半部分(bottom half)中断

void spin_lock_irq(spinlock_t *lock)

获取锁并禁止本地中断

spin_lock_irqsave(lock, flags)

宏,获取锁并保存本地中断状态(flags)

对应的有四个返回获取状态的加锁函数(或宏):

自旋锁接口

说明

int spin_trylock(spinlock_t *lock)

同spin_lock,在获取失败时返回非0

int spin_trylock_bh(spinlock_t *lock)

同spin_lock_bh,在获取失败时返回非0

int spin_trylock_irq(spinlock_t *lock)

同spin_lock_irq,在获取失败时返回非0

spin_trylock_irqsave(lock, flags)

同spin_lock_irqsave,在获取失败时返回非0

以及四个释放自旋锁函数:

自旋锁接口

说明

void spin_unlock(spinlock_t *lock)

释放锁

void spin_unlock_bh(spinlock_t *lock)

释放锁并开启下半部分中断

void spin_unlock_irq(spinlock_t *lock)

释放锁并打开本地中断

void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)

释放锁并恢复本地中断状态(flags)

除此之外,还有检测、等待锁函数:

自旋锁接口

说明

void spin_unlock_wait(spinlock_t *lock)

等待,直到释放锁

int spin_is_locked(spinlock_t *lock)

已上锁则返回非0,没有上锁则返回0

int spin_can_lock(spinlock_t *lock)

没有上锁则返回非0,上锁则返回0

这些函数最底层的调用是这样的:

#define__LOCK(lock) \
do { preempt_disable(); ___LOCK(lock); } while (0)
#define___LOCK(lock) \
do { __acquire(lock); (void)(lock); } while (0)
以及:
#define __UNLOCK(lock) \
do { preempt_enable(); ___UNLOCK(lock); } while (0)

spin_lock_irq函数在上锁时会先禁用抢占,然后获取锁;释放锁时先启用抢占,再释放锁。而spin_lock_irqsave更进一步,它会将本地的抢占状态保存起来,释放锁之后恢复原来的抢占状态,这样就避免了原来抢占被禁止而释放锁后抢占被打开的情况。

arch_spinlock的具体实现与体系结构有关,定义在<asm/Spinlock_types.h>下:

typedefstructarch_spinlock {
	union {
		__ticketpair_t head_tail;
		struct__raw_tickets {
			__ticket_t head, tail;
		} tickets;
	};
} arch_spinlock_t;

可以看出,自旋锁在硬件支持的基础上进一步封装,已经打包成了完整可用的锁结构。在内核代码的很多地方,特别是涉及到device操作的代码都会用到自旋锁。

自旋锁的调用

       自旋锁的调用方法如下:

DEFINE_SPINLOCK(lock);
spin_lock(&lock);
/* 临界区 */
spin_unlock(&lock);

例如,<sound/ppc/Beep.c>文件实现了蜂鸣器的控制接口,蜂鸣器事件函数为

staticint snd_pmac_beep_event(struct input_dev *dev, unsignedint type, unsignedint code, int hz)

该函数在snd_pmac_attach_beep函数内进行了注册:

input_dev->event = snd_pmac_beep_event;

函数内对蜂鸣器的控制语句:

structsnd_pmac *chip;
structpmac_beep *beep;
unsignedlong flags;
//…
if (!hz) {
	spin_lock_irqsave(&chip->reg_lock, flags);
	if (beep->running)
		snd_pmac_beep_stop(chip);
	spin_unlock_irqrestore(&chip->reg_lock, flags);
	return 0;
}

在这段代码中,先是获取chip的锁,将本地中断状态保存起来(flags),然后进入临界区,操作蜂鸣器beep,如果beep在running,即蜂鸣器在响,则停掉beep。然后退出临界区,从flags恢复本地中断状态。

       除了device操作,下面将要介绍的信号量的实现中也用到了自旋锁,具体内容将在后面详细说明。

       需要留意的是,自旋锁的临界区要尽可能简短,避免其他进程等待(占用CPU原地等待)太久。而且不能递归地调用自旋锁。递归调用自旋锁会导致死锁的发生。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值