SMP 与自旋锁
-
SMP 即对称多处理技术,在多核CPU上,各CPU之间共享内存和其他资源,但是只有一个操作系统,操作系统将各个任务均匀的分配该多个CPU执行,从而极大的提高系统的并行处理能力。
-
由于多个CPU之间的内存是共享的,当CPU使用共享资源时需要通过互斥机制获得其使用权,实现其互斥的方式有两种,一种是互斥锁,另一种是自旋锁,两者在同一时间都只能一个持有者,但是在工作方式上有很大区别,互斥锁在获取锁时如果发现资源不可用时会使调用线程进入睡眠,而自旋锁不同,获取互斥锁失败时会在原地等待资源可用,不会使调用线程进入睡眠。
-
单核计算机系统中多个进程访问同一个共享资源时通过屏蔽中断实现互斥,但是在多核计算机系统中,该方式不再有效,屏蔽中断只是让当前CPU不再响应中断请求,但是其他CPU仍然可以响应中断,为此引入了互斥锁,当系统中存在多个CPU时,竞争自旋锁的CPU首先屏蔽中断,然后通过自旋竞争获得共享资源的使用权,而竞争失败的CPU需要等待自旋锁被释放重新进行竞争使用权。
-
自旋锁适用于锁使用者持锁时间比较短的情况,正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。
spinlock 加锁
static ALWAYS_INLINE k_spinlock_key_t k_spin_lock(struct k_spinlock *l)
{
ARG_UNUSED(l);
k_spinlock_key_t k;
/* 关中断 */
k.key = arch_irq_lock();
#ifdef CONFIG_SPIN_VALIDATE
/* 确认当前CPU未获取到互斥锁 */
__ASSERT(z_spin_lock_valid(l), "Recursive spinlock %p", l);
# ifdef CONFIG_KERNEL_COHERENCE
__ASSERT_NO_MSG(z_spin_lock_mem_coherent(l));
# endif
#endif
#ifdef CONFIG_SMP
/* 自旋等待锁可用,获取失败继续尝试直到成功 */
while (!atomic_cas(&l->locked, 0, 1)) {
}
#endif
#ifdef CONFIG_SPIN_VALIDATE
/* 设置自旋锁持有持有线程和持有CPU */
z_spin_lock_set_owner(l);
#if defined(CONFIG_SPIN_LOCK_TIME_LIMIT) && (CONFIG_SPIN_LOCK_TIME_LIMIT != 0)
l->lock_time = sys_clock_cycle_get_32();
#endif /* CONFIG_SPIN_LOCK_TIME_LIMIT */
#endif/* CONFIG_SPIN_VALIDATE */
return k;
}
spinlock 解锁
static ALWAYS_INLINE void k_spin_unlock(struct k_spinlock *l,
k_spinlock_key_t key)
{
ARG_UNUSED(l);
#ifdef CONFIG_SPIN_VALIDATE
/* 确认锁的持有者为当前CPU和当前线程 */
__ASSERT(z_spin_unlock_valid(l), "Not my spinlock %p", l);
#if defined(CONFIG_SPIN_LOCK_TIME_LIMIT) && (CONFIG_SPIN_LOCK_TIME_LIMIT != 0)
uint32_t delta = sys_clock_cycle_get_32() - l->lock_time;
__ASSERT(delta < CONFIG_SPIN_LOCK_TIME_LIMIT,
"Spin lock %p held %u cycles, longer than limit of %u cycles",
l, delta, CONFIG_SPIN_LOCK_TIME_LIMIT);
#endif /* CONFIG_SPIN_LOCK_TIME_LIMIT */
#endif /* CONFIG_SPIN_VALIDATE */
#ifdef CONFIG_SMP
/* Strictly we don't need atomic_clear() here (which is an
* exchange operation that returns the old value). We are always
* setting a zero and (because we hold the lock) know the existing
* state won't change due to a race. But some architectures need
* a memory barrier when used like this, and we don't have a
* Zephyr framework for that.
*/
/* 自旋锁解锁 */
atomic_clear(&l->locked);
#endif
/* 开启中断 */
arch_irq_unlock(key.key);
}