这部分内容是linux设备驱动学习总结-keyPoints中的子集,独立出来方便查找。
1、linux产生竞态主要三种情况:中断、抢占、多处理器。
一个cpu运行在进程上下文或者中断上下文(且这段上下文是临界区域)的时候可能
被中断打断,且这个中断要访问临界资源;
被其它进程抢占,且这个进程要访问临界资源;
或者其它的cpu也要访问临界区域。
这个时候就会发生竞态。
2、cpu运行在进程上下文的情况
避免中断产生的竞态,可以提前关闭中断,处理完成后在开启中断;
避免抢占产生的竞态,可以使用spin_lock,因为自旋锁锁住的临界区域是不可抢占的,但是要求临界区域尽量的短。另外在关闭中断的情况下抢占的竞态也不会发生,因为linux内核的进程调度也依赖中断实现;
针对多处理器产生的竞态,通常也是使用spin_lock,或者互斥锁也可以,至于什么时候使用互斥锁,什么时候使用自旋锁,参考第5条。
(另外2.6.35后,取消了中断嵌套,所以中断与中断产生的竞态可以不用担心了)
3、cpu运行在中断上下文的情况。
中断和抢占产生的竞态都不需要考虑,因为,2.6.35后,中断嵌套被取消,且中断上下文的优先级要高于进程上下文(抢占本身是进程抢占,运行在进程上下文)。
需要考虑的是多处理器产生的竞态,参考上一条,多处理器产生的竞态可以用spin_lock或者互斥锁来避免,但是中断不允许睡眠(互斥锁在拿不到锁的情况下会进入睡眠)所以必须使用spin_lock。
4、spin_lock在单处理器的情况下,自动退化为互斥锁(宋宝华说的,不理解其中原委)。而实际上,spin_lock在单处理器情况下没有任何意义。
想象一下单处理器运行在进程上下文,spin_lock锁住的区域可能产生竞态的情况只有中断,如果中断中也要访问这段临界区域,假如中断拿不到锁,那就彻底挂了(因为他会自旋,且我们只有一个cpu),如果顺利拿到锁,说实在的在单处理器中没有任何意义。
5、什么情况下用自旋锁,什么情况下用互斥锁?
首先,在临界区较小,占用cpu时间较短,或者任务的实时性要求较高的情况下使用自旋锁,可避免CPU频繁调度产生的开销。
其次,有可能引起阻塞的临界区绝对不可以使用自旋锁,阻塞意味着进程切换,切换到的进程如果也要spin_lock加锁,死锁就产生了。
最后,在中断和软中断中的临界区域,尽量使用自旋锁(这种情况也符合第一条,因为中断是实时性要求较高的任务),如果一定要用互斥锁,也要try_lock,因为中断程序不能睡眠。