<Linux内核设计与实现>6-7-8章关于自旋锁同步中提出
”中断处理下半部的操作中使用自旋锁尤其需要小心:下半部处理和进程上下文共享数据时,由于下半部的处理可以抢占进程上下文的代码,
  所以进程上下文在对共享数据加锁前要禁止下半部的执行,解锁时再允许下半部的执行“
  以下为此处提出的注意点的个人理解,水平有限希望路过的大神能留下宝贵的点评 谢谢~
  1.首先看下in_interrupt()的定义:

#define in_interrupt()        (irq_count())

#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \

| NMI_MASK))


                 
  2.再看下preempt_count()的定义:
  #define preempt_count()    (current_thread_info()->preempt_count)
  preempt_count分4个域:NMI-HARDIRQ-SOFTIRQ-PREEMPT 整个字段跟抢占相关.当preempt_count!=0禁止抢断,
  否则允许抢断
 
  3.上文提到自旋锁,来看下自旋锁的操作

spin_lock(spinlock_t* lock)

{

raw_spin_lock(&lock->rlock);

}

raw_spin_lock(raw_spinlock_t* lock)

{

//抢占相关,

preempt_disable();

//x86上是锁总线操作

do_raw_spin_lock(lock);

}

preempt_disable()->inc_preempt_count()->add_preempt_count(1)->

#define add_preempt_count(val) \

do{ \

preempt_count() += (val); \

} while (0);


 执行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相关日志