同步 -- 自旋锁

基础学习--原子操作

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;

相关API的实现

调用逻辑:

spin_lock_* -> raw_spin_lock_* -> _raw_* -> arch_spin_lock(架构相关,qspinlock)

spin_lock_init

自旋锁的初始化:就是做个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)

spin_lock

加锁:直接加锁,如果成功则返回,失败,则进入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

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在自己的使用变量上面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值