irq_enter, irq_exit

来源:互联网 发布:三星9502支持4g网络吗 编辑:程序博客网 时间:2024/06/06 04:40
  1. 硬中断处理模型:
  2. fastcall unsigned int do_IRQ(struct pt_regs *regs)
  3. {
  4.         ...
  5.         irq_enter();

  6.         //handle external interrupt (ISR)
  7.         ...
  8.           irq_exit();

  9.         return 1;
  10. }

irq_enter:
#define irq_enter()                         
     do {                              
          account_system_vtime(current);          
          add_preempt_count(HARDIRQ_OFFSET);     
     } while (0)

可见irq_enter的实现中将preempt_count变量的HARDIRQ部分+1,记录硬件中断的次数;
这样irq_enter的就可以禁止抢占。preempt_count是进程调度时用到的.也就是系统会根据preempt_count的值来判断是否可以调度以及抢占。只有当preempt_count为0时才可以调度或抢占。这样的话在do_IRQ执行期间(并且在irq_exit执行preempt_count-1之前),是不允许内核抢占以及调度到其他进程的,这样不论是在irq_enter或者irq_exit函数执行过程中,current宏得到的都是同一个进程,获取的都是同一个进程的preempt_count,即中断前运行的进程当调用preempt_disable或add_preempt_count函数时都不可以进行调度,因为都会改变preempt_count的值为非0。所以irq_enter就是告诉系统,现在正在处理中断的上半部分工作,不可以进行调度。你可能会奇怪,既然此时的irq中断都是都是被禁止的,为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦驱动程序主动通过local_irq_enable打开了IRQ(其实在handle_IRQ_event中就打开了IRQ),而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq_enter,在本次嵌套中断返回时,内核不希望进行抢占调度,而是要等到最外层的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理。

irq_exit(硬中断执行完后,调用):


软中断的执行时机有好几个,其中从硬中断去的路径为:irq_exit->invoke_softirq(), 看的是in_interrupt(),也就是说如果在存在硬中断、或软中断中,都不会调用软中段处理函数;

内核代码中,中断号被传递到do_IRQ处理,do_IRQ调用__do_IRQ函数,__do_IRQ中调用handle_IRQ_event函数,其调用相应的中断处理函数进行中断处理;处理完成,返回到do_IRQ函数,之后调用irq_exit,在irq_exit函数中,调用do_softirq处理软中断。
irq_exit调用invoke_softirq,即do_softirq函数,do_softirq调用__do_softirq。在__do_softirq函数中,最多处理10个软中断,若软中断的个数超过10,则调用wakeup_softirq唤醒ksoftirqd内核线程进行软中断处理。

in_interrupt() 检查current_thread_info()->preempt_count的硬中断与软中断计数器(在irq_enter中设置),只要其中一个值为正数,函数就返回非零值,其中preempt_count中的硬中断计数,表示硬中断的嵌套次数,如果有的话(当在handle_IRQ_event中开中断后,如果有同种类型的中断发生,则再次进入do_IRQ函数,会首先执行irq_enter函数,将硬中断的preempt_count加1,然后其状态位上加上IRQ_PENDING标志,但是由于前一次中断处理中加上的IRQ_INPROGRESS没有被清除,因此这里无法清除IRQ_PENDING标志,因此action还是为null,这样就无法再次执行handle_IRQ_event函数。从而退出本次中断处理,返回上一次的中断处理函数中,即继续执行handle_IRQ_event函数。但返回前本次do_irq函数还是会执行完的,即要执行irq_exit函数,因为在上面执行irq_enter函数的时候将硬中断的preempt_count加1了,所以本次本次软中断就不再执行,因为还在硬中断上下文中,即硬中断还没有执行完,等嵌套的硬中断都执行完(即最后嵌套的硬中断在handle_IRQ_event执行完了,会执行退出函数irq_exit的,在这里就可以执行软中断了)并且本地安装了软中断,就可以执行软中断了)就返回到被嵌套的硬中断中,就返回到被嵌套的中断中,即软中断交给最后一次嵌套的硬中断执行完后执行。或者有软中断正在执行的话(preempt_count的软中断计数不等于0)就返回
preempt_count的软中断计数器是在__do_softirq中加1的(local_bh_disable),表示软中断正在执行,不允许通过新的硬中断(do_irq->irq_exit)再次进入软中断执行,从而避免软中断在同一个cpu的重入。然后在__do_softirq软中断处理完后再将preempt_count的软中断计数器减1,表示下次可通过新的硬中断(do_irq->irq_exit)再次进入软中断执行

local_softirq_pending检查本地CPU否有软中断在pending状态;

注意:这里只有两个条件同时满足时,才有可能调用 do_softirq() 进入软中断。也就是说确认当前所有硬件中断处理完成,且有硬件中断安装了软中断处理时理时才会进入

然后通过preempt_enable_no_resched判断是否需要重新调度,还是直接返回中断前的程序执行:
/**
*使抢占计数器减1
*/
#define preempt_enable_no_resched() 
do { 
     barrier(); 
     dec_preempt_count(); 
} while (0)

0 0
原创粉丝点击