一、概念
spinlock:自旋锁,Linux内核提供的一种用于保护临界资源的机制,特别是在多核系统中存在 进程与进程/进程与中断/中断与中断之间的并发访问场景。spinlock使用的是一种“忙等”机制,相对信号量获取如果阻塞会让出cpu行为,spinlock会一直占用cpu持续等待直到获取成功,优点是由于没有发生sche上下文切换,适用与中断,同时在频繁访问临界区效率更高,约束是临界区不能休眠。
典型接口:
1. spin_lock/spin_unlock: (1)行为:lock 1、禁止内核抢占preempt_disable(); 2、获取自旋锁;unlock:1、释放自旋锁 2、打开内核抢占。(2)适合场景:进程/软中断使用。 由于不会禁止本地中断,效率更高,但是也约束了不能在中断中访问并发访问。
2. spin_lock_irq/spin_unlock_irq(1)行为:lock1、关闭本地中断 2、禁止内核抢占preempt_disable(); 3、获取自旋锁;unlock:1、释放自旋锁 2、使能本地中断 3、打开内核抢占(2)适合场景:进程/软中断/中断 使用。默认unlock会打开本地中断,使用在本地中断常开的场景。
3. spin_lock_irqsave/spin_unlock_irqrestore(1) 行为:lock1、记录当前中断开关状态到flag,关闭本地中断 2、禁止内核抢占preempt_disable(); 3、获取自旋锁;unlock:1、释放自旋锁 2、通过flag恢复本地中断状态 3、打开内核抢占(2) 适合场景:进程/软中断/中断 使用。最安全的接口,相应的要注意flag在lock到unlock过程中不能被重写修改。
二、使用规则
1、锁在使用前必须进行初始化,可使用SPIN_LOCK_UNLOCKED在定义事静态初始化或者使用spin_lock_init(XX)进行初始化。
2、lock到unlock之间必须是原子操作,不能休眠。(常见可能休眠的函数:sleep、wait_event、down、vmalloc、kmalloc(非GFP_ATOMIC)、copy_from_user)
3、避免同时获取多个锁,如果无法避免必须保证所有代码以相同的顺序获取锁。
4、flags为防止lock到unlock过程被修改引起中断状态恢复异常,必须使用局部变量。
三、常见问题
1、加锁之前没有进行初始化引起死锁,流程保护不完整引起。
2、A-A死锁,同一cpu获取锁后未释放重复获取同一把锁。由于加锁层次不清楚,连续获取同一把锁,或者递归加锁。
3、ABBA死锁,两个cpu按照不同顺序获取多把锁引起死锁,设计问题。
4、加锁/释放缩 不配对引起长期持有锁挂死尝试获取锁的cpu。常见是获取缩后异常分支返回,宏函数返回没有unlock。
5、加锁后休眠/阻塞 引起挂死。
6、spinlock的flag使用全局变量,或者多处连续使用引起unlock时状态被改写,引发诡异死锁/中断响应问题。