<Linux内核设计与实现>6-7-8章关于自旋锁同步中提出
”中断处理下半部的操作中使用自旋锁尤其需要小心:下半部处理和进程上下文共享数据时,由于下半部的处理可以抢占进程上下文的代码,
所以进程上下文在对共享数据加锁前要禁止下半部的执行,解锁时再允许下半部的执行“
以下为此处提出的注意点的个人理解,水平有限希望路过的大神能留下宝贵的点评 谢谢~
1.首先看下in_interrupt()的定义:
2.再看下preempt_count()的定义:
#define preempt_count() (current_thread_info()->preempt_count)
preempt_count分4个域:NMI-HARDIRQ-SOFTIRQ-PREEMPT 整个字段跟抢占相关.当preempt_count!=0禁止抢断,
否则允许抢断
3.上文提到自旋锁,来看下自旋锁的操作
执行preempt_count() += (val); 后,spin_lock()使得current_thread_info()->preempt_count字段中的PREEMPT域不为零,进一步说,它禁止进程抢断。
结合上面的分析,来看下共享数据时可能发生抢占的情景:
1)进程与中断共享数据:中断没有task结构,不接受进程调度,用mutex等互斥睡去后,再也醒不来,因此只能选择自旋锁;
由于进程随时会被中断打断,自旋锁不能保护共享数据,因此进程在进入临界区前用spin_lock_irq屏蔽中断来替换spin_lock。
2)进程与tasklet共享数据:tasklet有时是在do_irq中运行的(虽然有时候由ksoftirqd运行),还未完全退出到中断恢复之后,所以
也是一睡就醒不过来;tasklet被执行的条件是if(!in_interrupt()&&local_softirq_pending()){...}。
如果进程被中断打断,执行到do_softirq中,进程还是被打断的状态,虽然上了互斥锁(即preempt_count的PREEMPT+1),但是按上文分析in_interrupt()
只使用NMI-HARDIRQ-SOFTIRQ这3个字段,因此顺利通过if判断,开始执行软中断,使得保护共享数据的原意失败。如果,进程为了保护共享数据,得调用local_bh_disable();
增加preempt_count:SOFTIRQ域的值,使!in_interrupt()失败;
3)tasklet-中断共享数据:只能屏蔽中断了吧
附注:IN_INTERRUPT的作用怎么看都觉得跟windows中的IRQL相似,等组织好语句,更新一篇IRQL相关日志