1、使用注意
1、由于自旋锁属于忙等待的方式,所以自旋锁适用于临界区耗时很小的情况,
2、如果临界区很大,使用自旋锁会非常耗费CPU性能,同一个CPU递归使用自旋锁会造成死锁,这一点跟互斥锁是一样的
3、在自旋锁锁定期间不能调用可能引起进程调度的函数,或者引起阻塞的函数,比如copy_to_user()、copy_from_user()、kmalloc()、msleep()等等函数,因为自旋锁上锁期间会禁止抢占,无法执行调度,所以会造成内核崩溃
4、由于spin_lock_irqsave并不能屏蔽别的CPU中断,所以进程上下文调用了spin_lock_irqsave和spin_unlock_irqrestore,中断里最好也调用spin_lock和spin_unlock
5、单核cpu,抢占式内核spin_lock期间内休眠或者阻塞引起调度,实测内核会崩溃,非抢占式内核spin_lock期间内因阻塞或休眠引起调度实际测试不会崩溃(不是死锁,spin_lock不是禁止抢占了吗,原因暂时未知,期待大神解答)
崩溃信息
BUG: scheduling while atomic: fasync_t/181/0x00000002
BUG: sleeping function called from invalid context at arch/arm/kernel/signal.c:502
in_atomic(): 1, irqs_disabled(): 0, pid: 181, name: fasync_t
Unable to handle kernel paging request at virtual address 76e9e574
pgd = 8f304000
[76e9e574] *pgd=0f2de831, *pte=00000000, *ppte=00000000
Internal error: Oops: 80000007 [#1] PREEMPT ARM
Modules linked in: fasync_d(O)
CPU: 0 PID: 181 Comm: fasync_t Tainted: G W O 4.19.91 #80
Hardware name: Novatek Video Platform
PC is at 0x76e9e574
LR is at 0x0001057c
pc : [<76e9e574>] lr : [<0001057c>] psr: 60070010
sp : 7eca9270 ip : 76e9e574 fp : 7eca927c
r10: 76fb4000 r9 : 00000000 r8 : 00000000
r7 : 00000003 r6 : 00010470 r5 : 76fb1bf0 r4 : 00010630
r3 : 00000000 r2 : 00000001 r1 : 7eca9597 r0 : 000106a4
Flags: nZCv IRQs on FIQs on Mode USER_32 ISA ARM Segment user
Control: 10c53c7d Table: 0f304059 DAC: 00000055
Process fasync_t (pid: 181, stack limit = 0xc4a6a132)
Stack: (0x7eca9270 to 0x8f1e6000)
信息显示原子操作的时候使用了sleep,引起了调度
spin_lock的设计原则,spin_lock的代码必须是原子性的,不能因为任何原因放弃CPU
2、自旋锁内为什么不能用会引起调度的睡眠和阻塞函数
跟踪一下spin_lock(&mr_lock)的实现
#define spin_lock(lock) _spin_lock(lock)
#define _spin_lock(lock) __LOCK(lock)
#define __LOCK(lock) \ do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)
注意到“preempt_disable()”,这个调用的功能是“关抢占”(在spin_unlock中会重新开启抢占功能)。从 中可以看出,使用自旋锁保护的区域是工作在非抢占的状态;即使获取不到锁,在“自旋”状态也是禁止抢占的。了解到这,我想咱们应该能够理解为何自旋锁保护 的代码不能睡眠了。试想一下,如果在自旋锁保护的代码中间睡眠,此时发生进程调度,则可能另外一个进程会再次调用spinlock保护的这段代码。而我们 现在知道了即使在获取不到锁的“自旋”状态,也是禁止抢占的,而“自旋”又是动态的,不会再睡眠了,也就是说在这个处理器上不会再有进程调度发生了(自旋锁拿不到锁是忙等待的,睡眠的进程这个时候没释放锁,新进程就一直拿不到锁,cpu不会调度任务),那么 死锁自然就发生了。