- typedef struct {
- int counter;
- } atomic_t;
-
-
- static __always_inline void
- atomic_set(atomic_t *v, int i)
- {
- instrument_atomic_write(v, sizeof(*v));
- raw_atomic_set(v, i);
- }
-
-
- static __always_inline void
- raw_atomic_set(atomic_t *v, int i)
- {
- arch_atomic_set(v, i);
- }
-
- #define arch_atomic_read(v) __READ_ONCE((v)->counter)
- #define arch_atomic_set(v, i) __WRITE_ONCE(((v)->counter), (i))
-
- #define __WRITE_ONCE(x, val)
- do {
- *(volatile typeof(x) *)&(x) = (val); //typeof返回变量的类型
- } while (0)
spinlock -> raw_spinlock -> arch_spinlock_t(qspinlock)
- typedef struct spinlock {
- union {
- struct raw_spinlock rlock; //自旋锁的核心成员是raw_spinlock锁
- };
- } spinlock_t;
-
-
- typedef struct raw_spinlock {
- arch_spinlock_t raw_lock; //raw_lock的核心成员是arch_spinlock_t,它与具体架构有关
- } raw_spinlock_t;
-
-
- //ARM64位架构中,为qspinlock
- typedef struct qspinlock {
- union {
- atomic_t val; //原子变量
- #ifdef __LITTLE_ENDIAN
- struct {
- u8 locked; //最优先持锁标志,即当unlock之后,这个位被置位的CPU最先持锁,1和0
- u8 pending; //表示这个锁是否被人持有,1被人持有,0无人持锁
- };
- struct {
- u16 locked_pending; //由locked 和 pending构成
- u16 tail; //由idx CPU构成,用来标识等待队列最后一个节点
- };
- } arch_spinlock_t;
调用逻辑:
spin_lock_* -> raw_spin_lock_* -> _raw_* -> arch_spin_lock(架构相关,qspinlock)
自旋锁的初始化:就是做个spinlock->raw_spinlock->arch_spinlock_t的转换,然后把arch_spinlock_t的val初始化为0
- include\linux\spinlock_types_up.h
-
- # define spin_lock_init(_lock)
- do {
- spinlock_check(_lock);
- *(_lock) = __SPIN_LOCK_UNLOCKED(_lock);
- } while (0)
加锁:直接加锁,如果成功则返回,失败,则进入qspinlock的具体实现方式上
- static __always_inline void spin_lock(spinlock_t *lock)
- {
- raw_spin_lock(&lock->rlock);
- }
-
- static inline void __raw_spin_lock(raw_spinlock_t *lock)
- {
- preempt_disable();//关抢占
- spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
- }
- #define spin_lock_irqsave(lock, flags) \
- do { \
- raw_spin_lock_irqsave(spinlock_check(lock), flags); \
- } while (0)
-
- static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
- {
- unsigned long flags;
-
- local_irq_save(flags);//关本地中断,并保存中断状态
- preempt_disable();//关抢占
- spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
- return flags;
- }
-
-
- #define arch_spin_lock(l) queued_spin_lock(l)
- #define arch_spin_trylock(l) queued_spin_trylock(l)
-
- #ifndef queued_spin_lock
-
- static __always_inline void queued_spin_lock(struct qspinlock *lock)
- {
- int val = 0;
-
- if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)))
- return;
-
- queued_spin_lock_slowpath(lock, val);//这里就是直接获取锁失败的情况,需要自旋等待了。
- }
-
qspinlock的实现是建立在MCS锁的理论基础上。
- struct mcs_spinlock {
- struct mcs_spinlock *next;
- int locked;
- };
mcs_spinlock中next成员就是构建单链表的基础,spin等锁的操作只需要将所属自己CPU的mcs_spinlock结构体加入单链表尾部,然后spin,直到自己的mcs_spinlock的locked成员置1(locked初始值是0)。unlock的操作也很简单,只需要将解锁的CPU对应的mcs_spinlock结构体的next域的lock成员置1,相当于通知下一个CPU退出循环。
以4个CPU的系统为例说明。首先CPU0申请spinlock时,发现链表是空,并且锁是释放状态。所以CPU0获得锁。
CPU1继续申请spinlock,需要spin等待。所以将CPU1对应的mcs_spinlock结构体加入单链表尾部。然后spin等待CPU1对应的mcs_spinlock结构体locked成员被置1。
当CPU2继续申请锁时,发现链表不为空,说明有CPU在等待锁。所以也将CPU2对应的mcs_spinlock结构体加入链表尾部。
当CPU0释放锁的时候,发现CPU0对应的mcs_spinlock结构体的next域不为NULL,说明有等待的CPU。然后将next域指向的mcs_spinlock结构体的locked成员置1,通知下个获得锁的CPU退出自旋。MCS lock头指针可以选择不更新,等到CPU2释放锁时更新为NULL。
通过以上步骤,我们可以看到每个CPU都spin在自己的使用变量上面。