spinlock总结

spinlock_t的结构体分3层,arch_spinlock_t为最底层跟架构相关。

typedef struct spinlock {
	union {
		struct raw_spinlock rlock;
		。。。
	};
} spinlock_t;
typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
	。。。
} raw_spinlock_t;
typedef struct {
	union {
		u32 slock;
		struct __raw_tickets {
#ifdef __ARMEB__   /* 大端 */
			u16 next;
			u16 owner;
#else
			u16 owner;
			u16 next;
#endif
		} tickets;
	};
} arch_spinlock_t;

spinlock的原理就是比较next和owner。目的是保证唤醒的公平性,先到先唤醒,后到后唤醒。过程类似银行取号办业务,第一个客户从next变量中取一个号码,他的next==0,他会一直保存该号码牌直到业务办理完成;无论该客户是否能立马办理业务,都会将next加1,即其他客户取的号码只能排在他后面,所以如果有多个客户,他们获取到的编号依次为0,1,2,3,…;工作人员办理业务首先呼叫0号,对应号码的客户办理业务;办理完成后工作人员再呼叫2号,依次类推…。

  1. CPU0 获取lock
    1.1.保存当前的next和owner到局部变量
    1.2.将全局next加1.
    1.3.比较旧的next和owner,如果相等,表示获取lock成功,继续执行。

  2. CPU1 获取lock
    2.1.保存当前参数:next=1,owner=0
    2.2.将全局变量next加1.
    2.3.比较保存的next和owner,因为不等,所以执行wfe指令进入低功耗
    2.4.如何被其他cpu唤醒了,会再次将保存的next和全局的owner做比较:
    2.4.1.如果相等,则继续往下执行
    2.4.2.如果不等,则再次执行WFE进入低功耗等带自己的叫号。

  3. CPU0 释放lock
    3.1.将全局变量lock.tickets.owner加1(叫号下一个).
    3.2.执行sev指令,唤醒所有进入低功耗的CPU

    对onwer和next的操作采用独占指令,并且获取spinlock前会关闭抢占功能,释放spinlock执行使能抢占功能

调用spin_lock函数最后会调用arch_spin_lock函数:

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
	unsigned long tmp;
	u32 newval;
	arch_spinlock_t lockval;

	prefetchw(&lock->slock);
	__asm__ __volatile__(
"1:	ldrex	%0, [%3]\n"  /* 保存lock->slock也就是lock->tickets.owner 和lock->tickets.next */
"	add	%1, %0, %4\n"  /* 高16位加一 */
"	strex	%2, %1, [%3]\n" /* 回写到lock->slock */
"	teq	%2, #0\n" /* 保证strex指令成功 */
"	bne	1b"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
	: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
	: "cc");
	/* 通过while循环来检查是否轮到该cpu获得lock */
	while (lockval.tickets.next != lockval.tickets.owner) {
		wfe();
		lockval.tickets.owner = READ_ONCE(lock->tickets.owner); /* 每次从低功耗醒来获取最新的owner值 */
	}

	smp_mb();
}

arch_spin_lock函数在多CPU模式下主要做两件事:
1.获取保存lock->tickets.next
2.lock->tickets.next加1
3.判断更新前的next和owner
4.如果不等,则调用wfe指令进入低功耗
5.如果相等,则执行smp_mb

释放lock的底层函数:

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
	smp_mb();
	lock->tickets.owner++;
	dsb_sev();
}

lock->tickets.owner++;语句用于叫号下一个进程或者cpu
dsb_sev函数会执行sev指令,唤醒所有低功耗的cpu

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值