Linux Kernel中spinlock的设计与实现

👉👉👉 个人博客笔记导读目录(全部) 👈👈👈
.
说明:
在默认情况下,本文讲述的都是ARMV8-aarch64架构,linux kernel 5.14

背景

早期spinlock的设计

早期的spinlock的设计是锁的拥有者加锁时将锁的值设置为1,释放锁时将锁的值设置为0,这样做的缺点是会出现 先来抢占锁的进程一直抢占不到锁,而后来的进程可能一来 就能获取到锁。导致这个原因的是先抢占的进程和后抢占的进程在抢占锁时并没有一个先后关系,最终就是离锁所在的内存最近的cpu节点就有更多的机会抢占锁,离锁所在内存远的节点可能一直抢占不到。

新版spinlock设计

为了解决这个spinlock的不公平问题,linux 2.6.25内核以后,spinlock采用了一种"FIFO ticket-based"算法的spinlock机制,可以很好的实现先来先抢占的思想。具体的做法如下:

  • (1)、spinlock的核心字段有owner和next,在初始时,owner=next=0
  • (2)、当第一个进程抢占spinlock时,会在进程函数本地保存下next的值,也就是next=0,并将spinlock的next字段加1;
  • (3)、当获取spinlock的进程的本地next和spinlock的owner相等时,该进程就获取到spinlock;
  • (4)、由于第一个进程本地的next=0,并且spinlock的owner为0,所以第一个CPU获取到spinlock;
  • (5)、接着当第二个进程抢占spinlock,此时spinlock的next值为1,保存到本地,然后将spinlock的next字段加1。而spinlock的owner字段依然为0,第二个进程的本地next 不等于spinlock的owner,所以一直自旋等待spinlock;
  • (6)、第三个进程抢占spinlock,得到本地next值为2,然后将spinlock的next字段加1。此时spinlock的owner字段还是为0,所以第三个进程自旋等待。
  • (7)、当第一个进程处理完临界区以后,就释放spinlock,执行的操作是将spinlock的owner字段加1;
  • (8)、由于第二个进程和第三个进程都还在等待spinlock,他们会不停第获取spinlock的owner字段,并和自己本地的next值进行比较。当第二个进程发现自己的next值和spinlock的owner字段相等时(此时next == owner == 2),第二个进程就获取到spinlock。第三个进程的本地next值是3,和spinlock的owner字段不相等,所以继续等待;
  • (9)、只有在第二个进程释放了spinlock,就会将spinlock的owner字段加1,第三个进程才有机会获取spinlock。

我在举个例子,如下:
在这里插入图片描述

T1 : 进程1调用spin_lock,此时next=0, owner=0获得该锁,在arch_spin_lock()底层实现中,会next++
T2 : 进程2调用spin_lock,此时next=1, owner=0没有获得该锁,while(1)中调用wfe指令standby在那里,等待owner==next成立.
T3 : 进程3调用spin_lock,此时next=2, owner=0没有获得该锁,while(1)中调用wfe指令standby在那里,等待owner==next成立.
T4&T5 : 进程1调用spin_unlock,此时owner++,即owner=1,接着调用sev指令,让进程2和进程3退出standby状态,走while(1)流程,重新检查owner==next条件。此时进程2条件成立,进程3继续等待。进程2获得该锁,进程3继续等待。

Linux Kernel中的SpinLock的实现

在这里插入图片描述

(linux/include/linux/spinlock.h)

static __always_inline void spin_unlock(spinlock_t *lock)
{
	raw_spin_unlock(&lock->rlock);
}


static __always_inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}
(linux/include/linux/spinlock.h)

#define raw_spin_lock_irq(lock)		_raw_spin_lock_irq(lock)
#define raw_spin_lock_bh(lock)		_raw_spin_lock_bh(lock)
#define raw_spin_unlock(lock)		_raw_spin_unlock(lock)
#define raw_spin_unlock_irq(lock)	_raw_spin_unlock_irq(lock)


#define raw_spin_lock(lock)	_raw_spin_lock(lock)

(linux/kernel/locking/spinlock.c)

#ifdef CONFIG_UNINLINE_SPIN_UNLOCK
void __lockfunc _raw_spin_unlock(raw_spinlock_t *lock)
{
	__raw_spin_unlock(lock);
}
EXPORT_SYMBOL(_raw_spin_unlock);
#endif

#ifndef CONFIG_INLINE_SPIN_LOCK
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{
	__raw_spin_lock(lock);
}
EXPORT_SYMBOL(_raw_spin_lock);
#endif
(linux/include/linux/spinlock_api_smp.h)

static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
	spin_release(&lock->dep_map, _RET_IP_);
	do_raw_spin_unlock(lock);
	preempt_enable();
}

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
	preempt_disable();
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
(linux/include/linux/spinlock.h)

static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
{
	mmiowb_spin_unlock();
	arch_spin_unlock(&lock->raw_lock);
	__release(lock);
}

static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
	__acquire(lock);
	arch_spin_lock(&lock->raw_lock);
	mmiowb_spin_lock();
}
(linux/include/asm-generic/qspinlock.h)

#define arch_spin_is_locked(l)		queued_spin_is_locked(l)
#define arch_spin_is_contended(l)	queued_spin_is_contended(l)
#define arch_spin_value_unlocked(l)	queued_spin_value_unlocked(l)
#define arch_spin_lock(l)		queued_spin_lock(l)
#define arch_spin_trylock(l)		queued_spin_trylock(l)
#define arch_spin_unlock(l)		queued_spin_unlock(l)

queued_spin_lock等的实现在linux/kernel/locking/qspinlock.c,不是一般的复杂,我们就不贴代码分析了。总之底层用到了ldxrstxr等exclusive独占的指令

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码改变世界ctw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值