Chinaunix首页 | 论坛 | 博客
  • 博客访问: 667033
  • 博文数量: 209
  • 博客积分: 26
  • 博客等级: 民兵
  • 技术积分: 326
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-21 09:29
文章分类

全部博文(209)

文章存档

2015年(6)

2014年(40)

2013年(154)

2012年(11)

我的朋友

分类: LINUX

2014-07-18 14:50:06

 

内核中断-2014.7

1.   arm平台的中断

arm核心拥有2个外部中断线,IRQFIQ;这两根中断线连接到中断控制器上;中断控制器(IC)利用IRQ/FIQarm核报告外部中断的产生;IC在上报中断之前,如果同时有多个中断产生,就要按照优先级进行排队,把优先级最高的中断送至“当前服务寄存器”,也就是报告给arm核,“当前服务寄存器”需要在中断服务代码中(在内核的中断服务公共代码中)进行清除,以允许新的中断到来;很可能上一个中断服务程序还没有处理完毕,下一个优先级更高的中断就到来了,这样上一个中断服务程序就会被打断,然后执行优先级高的中断服务程序;

2.   中断嵌套

相同中断号的中断服务程序是绝对不可能嵌套的,原因(1)IC的中断排队寄存器中,优先级小于等于当前服务中断号的中断是不可能被报告给arm核的;(2)好像在linux内核代码中会使用disable_irq之类的函数禁止当前的中断?;总之,同一个中断服务程序是不会发生嵌套的,嵌套只发生在高优先级的中断打断低优先级的中断,从而发生嵌套;

3.   中断不可打断/中断不可调度/中断不能睡眠…..的解释

网上对这类问题的解释太混乱,这里针对arm-linux做出解释:(1)中断上半部是可以睡眠的,但是睡眠会使得内核变得非常复杂,难以追踪代码执行路径,所以强烈不支持在上半部使用会睡眠的函数,这样设计也是为了使得内核变得简单;(2)中断上半部确实不可以被调度,因为它不是一个可以被调度的实体,例如进程或者内核线程;事实上,应该根本就没有“中断可否调度”之类的问题;(3)中断上半部可以被打断,但是只能被优先级更高的中断打断,被打断后就进入高优先级中断的服务程序,也就是实现了中断嵌套;可以使用local_irq_ disable()或者local_irq_save()或者spin_lock_irqsave()禁止本CPU核的所有中断,也就是禁止IRQ线以及FIQ线,这样就不会发生中断的嵌套了;

4.   发生中断嵌套时的内核状态

首先看一段代码

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

  struct pt_regs *old_regs = set_irq_regs(regs);

  irq_enter();

  if (unlikely(irq >= NR_IRQS)) {

         if (printk_ratelimit())

                printk(KERN_WARNING "Bad IRQ%u\n", irq);

         ack_bad_irq(irq);

  } else {

         generic_handle_irq(irq);

  }

  irq_finish(irq);

  irq_exit();

  set_irq_regs(old_regs);

}

这是arm平台下处理中断的开始点,每产生一个中断都会调用这个函数;irq_enter()就让内核执行路径进入了中断上下文;irq_exit()让内核退出中断上下文;所有的中断上半部都是在generic_handle_irq(irq)中被调用的,也就很明确的表明了使用request_irq()注册的中断服务程序是运行于中断上下文中的;当发生中断嵌套,假定只有2层嵌套,当高优先级中断服务程序返回后,内核会如何处理呢?irq_enter的作用是禁止抢占,是通过把preempt_count加上HARDIRQ_OFFSET,HARDIRQ_OFFSET代表中断的上半部,preempt_count是进程调度时用到的。也就是系统会根据preempt_count的值来判断是否可以调度。只有当preempt_count0时才可以调度。当调用preempt_disableadd_preempt_count函数时都不可以进行调度,因为都会改变preempt_count的值为非0
所以irq_enter就是告诉系统,现在正在处理中断的上半部分工作,不可以进行调度。你可能会奇怪,既然此时的irq中断都是都是被禁止的,为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦驱动程序主动通过local_irq_enable打开了IRQ[2014.7.8日,事实上,在驱动中注册的中断函数执行时,如果不使用local_irq_disable,中断总是打开的,就是为了允许中断嵌套],而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq_enter,在本次嵌套中断返回时,preempt_count不为0内核不希望进行抢占调度,而是要等到最外层的被打断的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理

5.   一句话总结上半部

中断嵌套的整个过程都是处于中断上下文中;一旦处于中断上下文,或者说正在执行中断处理,那么当前的CPU核上是不可以被调度一个进程来运行的,由中断处理程序独占,只能被更高优先级的中断打断嵌套;中断上半部可以嵌套;……………注意,前面说的都是中断上半部!!!

6.   下半部

下半部虽说有softirqtaskletworkqueue三种机制,但一般只用taskletworkqueueworkqueue 就是一个普通的内核线程,所以它想睡眠就睡眠,任何内核线程能做的事它都可以做,这样的灵活性的代价就是它的效率比tasklet差;tasklet也是工作在中断上下文,下面专门讨论tasklet

7.   tasklet

同一个tasklet多次调度可能只会执行一次,所以需要在这种下半部中注意这种问题,比如对控制器的重置就可以使用tasklet,因为多次重置和一次重置的效果是完全一样的,由于do_softirq会判断当前是否处于软中断上下文,如果是,则直接退出不执行tasklet代码;一个tasklet同时只能在一个核上运行,不同的tasklet可以在多核上同时执行;tasklet应该继承了softirq的特点,谁(CPU)调度,谁执行;tasklet处于中断上下文,所以也和普通中断一样,是独占CPU,但是会被优先级更高硬件中断IRQ打断,但是tasklet不会打断tasklet;其实还有很多问题不够明白!!!!!!但写驱动足以;

8.   关于软中断/tasklet的调用时刻

一般在中断上半部中会调度tasklet,上半部执行完后,在退出中断上下文时

void irq_exit(void)

{……………………………….

preempt_count_sub(HARDIRQ_OFFSET);

if (!in_interrupt() && local_softirq_pending())

           invoke_softirq();

…………………………….

}

会用invoke_softirq来尝试运行被激活的软中断,接着__do_softirq,在这里,会关闭软中断并开启硬件(也就是上半部)中断,也就是印证了上面那句话,在同一个cpu上,软中断不可以被软中断打断,但是可以被硬件中断(也就是上半部)打断。

这里,是不是还可以表明,taskletworkqueue作为下半部而言,tasklet会更快的得到执行。Workqueue应该是在irq_exit结束之后,退出中断上下文后,才有可能被调度。

阅读(1436) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~