Linux内核锁

spinlock

单CPU系统

  include/linux/spinlock_api_up.h

#define __LOCK(lock) \
	do { preempt_disable(); ___LOCK(lock); } while (0)
  
#define __UNLOCK(lock) \
	do { preempt_enable(); ___UNLOCK(lock); } while (0)

多CPU系统

  include/linux/spinlock_api_smp.h

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
	preempt_disable();
	... ...
}

static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
	... ...
	preempt_enable();
}

semaphore

  kernel/locking/semaphore.c

void down(struct semaphore *sem)
{
	... ...
	raw_spin_lock_irqsave(&sem->lock, flags);
	... ...
	raw_spin_unlock_irqrestore(&sem->lock, flags);
} // spinlock包含抢占的关闭/打开

mutex

  kernel/locking/mutex.c

// __mutex_lock_slowpath
static __always_inline int __sched
__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
                    struct lockdep_map *nest_lock, unsigned long ip,
                    struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
{
	... ...
	preempt_disable();	// 关闭抢占
	... ...
	if (mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx)) {
		/* got the lock, yay! */
		preempt_enable();
		return 0;
	}
	... ...
	spin_lock_mutex(&lock->wait_lock, flags); // 关闭抢占
	... ...
	for (;;) {
		... ...
		spin_unlock_mutex(&lock->wait_lock, flags);
		schedule_preempt_disabled();
		spin_lock_mutex(&lock->wait_lock, flags);
	}
	... ...
        
skip_wait:
	... ...
	spin_unlock_mutex(&lock->wait_lock, flags); // 打开抢占
	preempt_enable();	// 打开抢占
	return 0;

err:
	... ...
	spin_unlock_mutex(&lock->wait_lock, flags); // 打开抢占
	... ...
	preempt_enable(); // 打开抢占
	return ret;
}

理解

  1. spinlock临界区中关闭抢占。如果打开抢占,临界区内发生中断,中断返回时会检查抢占调度:
    - 抢占调度的进程也可能申请spinlock,导致死锁
    - 即使没有死锁,抢占调度意味持有spinlock的进程睡眠,不符合忙等待的语义(不能睡眠和快速完成)
    因此,spinlock临界区代码不允许抢占,即不能睡眠和主动调度。

  2. spinlock虽然关闭抢占,但并不一定关闭中断。如果临界区内发生中断,而中断处理程序也要申请spinlock,中断上下文进入忙等待状态,导致死锁。因此,对可能在中断处理程序中操作的临界区加锁,需要使用spin_lock_irq和spin_lock_irqsave:

static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
{
	local_irq_disable();
	... ...
}

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
	unsigned long flags;
	/* 调用local_irq_disable
	 * 只关闭本地CPU的中断
	 * 因为即使其他CPU的中断处理程序试图获取spinlock,本地CPU的临界区也在继续执行,很快会离开临界区并释放锁(忙等待的语义),其他CPU的中断处理程序可以很快获取spinlock */
	local_irq_save(flags);
	... ...
}
  1. semaphore和mutex虽然在申请/释放锁的过程中不允许抢占(schedule除外),但临界区中允许抢占:
    - 如果临界区内被抢占调度,抢占调度的进程在等待获取锁的过程中会睡眠(schedule),被抢占的临界区理论上可以被重新唤醒
    - 但是不允许递归和嵌套(未找到semaphore不可以递归和嵌套的说法),应该是会导致死锁。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值