目录
之前 《转载 Linux 内核同步(二):自旋锁(Spinlock)》文章是 ARMv7 架构以及 Linux 4.x 的自旋锁相关的原理和实现,最近在看 ARMv8 + Linux 5.x 相关的内容,所以稍微更新一点东西;
他们之间的主要区别在于处理器架构的变化,之前的独占指令有些变化,导致最后和处理器相关的调用部分有些许更新,同时也在之前的基础之上新增了部分插图,来更加清晰的说明情况;
1、资源竞争
首先说明导致竞争的所有情况:
1、单个 CPU 上多个任务访问同一个资源,导致竞争:
2、单个 CPU 上任务与中断访问同一个资源,导致竞争:
3、SMP,多个任务访问同一个资源,导致竞争:
4、SMP,任务和中断访问同一个资源,导致竞争:
5、SMP,中断访问同一个资源,导致竞争:
2、自旋锁
自旋锁的原理是,尝试获取锁,如果获取不到,那么就原地“自旋”,说是自旋,其实实现上使用了 WFE 指令,让 CPU 进入等待状态,当另外一个持有锁的释放后,使用 SEV 唤醒处于 WFE 等待状态下的 CPU;
自旋锁的使用要求是:不允许睡眠,并且不应该被长时间的持有;
spin_lock 和 spin_unlock 自旋锁在实现上,是需要关闭 CPU 抢占的,原因如下图:
进程 A 拿到了锁,还未释放的时候,被中断唤醒了进程 B,想去拿锁,就导致死锁;
如果要在中断上下文使用自旋锁呢?不仅仅需要关闭抢占,还需关闭中断,否则就会出现如下死锁:
所以中断上下文使用 spin_lock_irq/spin_unlock_irq(软中断上下文使用 spin_lock_bh/spin_unlock_bh),他和 spin_lock 不一样的地方在于,它关闭了中断;
自旋锁最底层的实现,是和处理器相关的,处理器需要提供独占指令,以便可以进行独占访问,在 ARM64 上实现如下:
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned int tmp;
arch_spinlock_t lockval, newval;
asm volatile(
/* Atomically increment the next ticket. */
ARM64_LSE_ATOMIC_INSN(
/* LL/SC */
" prfm pstl1strm, %3\n"
"1: ldaxr %w0, %3\n"
" add %w1, %w0, %w5\n"
" stxr %w2, %w1, %3\n"
" cbnz %w2, 1b\n",
/* LSE atomics */
" mov %w2, %w5\n"
" ldadda %w2, %w0, %3\n"
__nops(3)
)
/* Did we get the lock? */
" eor %w1, %w0, %w0, ror #16\n"
" cbz %w1, 3f\n"
/*
* No: spin on the owner. Send a local event to avoid missing an
* unlock before the exclusive load.
*/
" sevl\n"
"2: wfe\n"
" ldaxrh %w2, %4\n"
" eor %w1, %w2, %w0, lsr #16\n"
" cbnz %w1, 2b\n"
/* We got the lock. Critical section starts here. */
"3:"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
: "memory");
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
unsigned long tmp;
asm volatile(ARM64_LSE_ATOMIC_INSN(
/* LL/SC */
" ldrh %w1, %0\n"
" add %w1, %w1, #1\n"
" stlrh %w1, %0",
/* LSE atomics */
" mov %w1, #1\n"
" staddlh %w1, %0\n"
__nops(1))
: "=Q" (lock->owner), "=&r" (tmp)
:
: "memory");
}
内嵌汇编代码,使用独占指令进行操作;
这里定义了自旋锁的占用规则,使用 FIFO tickets 机制(owner + next),即先来先拿锁,后来的按照先后顺序进行排队,在线发牌(next);
owner 指向了当前哪个拿到牌子的人,next 指向下一个拿到牌子的: