本篇说清楚自旋锁
读本篇之前建议先读系列篇 进程/线程篇.
内核中哪些地方会用到自旋锁?看图:
概述
自旋锁
顾名思义,是一把自动旋转的锁,这很像厕所里的锁,进入前标记是绿色可用的,进入格子间后,手一带,里面的锁转个圈,外面标记变成了红色表示在使用,外面的只能等待.这是形象的比喻,但实际也是如此.
在多CPU核环境中,由于使用相同的内存空间,存在对同一资源进行访问的情况,所以需要互斥访问机制来保证同一时刻只有一个核进行操作,自旋锁就是这样的一种机制。
-
自旋锁是指当一个线程在获取锁时,如果锁已经被其它
CPU
中的线程获取,那么该线程将循环等待,并不断判断是否能够成功获取锁,直到其它CPU
释放锁后,等锁CPU才会退出循环。 -
自旋锁的设计理念是它仅会被持有非常短的时间,锁只能被一个任务持有,而且持有自旋锁的CPU是不可以进入睡眠模式的,因为其他的CPU在等待锁,为了防止死锁上下文交换也是不允许的,是禁止发生调度的.
-
自旋锁与互斥锁比较类似,它们都是为了解决对共享资源的互斥使用问题。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个持有者。但是两者在调度机制上略有不同,对于互斥锁,如果锁已经被占用,锁申请者会被阻塞;但是自旋锁不会引起调用者阻塞,会一直循环检测自旋锁是否已经被释放。
虽然都是共享资源竞争,但自旋锁强调的是CPU
核间的竞争,而互斥量强调的是任务(包括同一CPU核)之间的竞争.
自旋锁长什么样?
typedef struct Spinlock {//自旋锁结构体
size_t rawLock;//原始锁
#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) // 死锁检测模块开关
UINT32 cpuid; //持有锁的CPU
VOID *owner; //持有锁任务
const CHAR *name; //锁名称
#endif
} SPIN_LOCK_S;
结构体很简单,里面有个宏,用于死锁检测,默认情况下是关闭的.所以真正的被使用的变量只有rawLock一个.但C语言代码中找不到变量的变化过程,而是通过一段汇编代码来实现.看完本篇会明白也只能通过汇编代码来实现自旋锁.
自旋锁使用流程
自旋锁用于多CPU核的情况,解决的是CPU之间竞争资源的问题.使用流程很简单,三步走。
-
创建自旋锁:使用
LOS_SpinInit
初始化自旋锁,或者使用SPIN_LOCK_INIT
初始化静态内存的自旋锁。 -
申请自旋锁:使用接口
LOS_SpinLock
LOS_SpinTrylock
LOS_SpinLockSave
申请指定的自旋锁,申请成功就继续往后执行锁保护的代码;申请失败在自旋锁申请中忙等,直到申请到自旋锁为止。 -
释放自旋锁:使用
LOS_SpinUnlock
LOS_SpinUnlockRestore
接口释放自旋锁。锁保护代码执行完毕后,释放对应的自旋锁,以便其他核申请自旋锁。
几个关键函数
自旋锁模块由内联函数实现,见于los_spinlock.h
代码不多,主要是三个函数.
ArchSpinLock(&lock->rawLock);
ArchSpinTrylock(&lock->rawLock)
ArchSpinUnlock(&lock->rawLock);
可以说掌握了它们就掌握了自旋锁,但这三个函数全由汇编实现.见于los_dispatch.S
文件
因为系列篇已有两篇讲过汇编代码,所以很容易理解这三段代码.函数的参数由r0记录,即r0保存了lock->rawLock
的地址,拿锁/释放锁是让lock->rawLock
在0,1切换
下面逐一说明自旋锁的汇编代码.
ArchSpinLock 汇编代码
FUNCTION(ArchSpinLock) @死守,非要拿到锁
mov r1, #1 @r1=1
1: @循环的作用,因SEV是广播事件.不一定lock-&g