您的位置首页>企业动态>

linux的中断线程化实现

导读 大家好,我是极客范的本期栏目编辑小友,现在为大家讲解linux的中断线程化实现问题。2 6 25 8内核实现了中断线程化,内核为每一个中断向量

大家好,我是极客范的本期栏目编辑小友,现在为大家讲解linux的中断线程化实现问题。

2.6.25.8内核实现了中断线程化,内核为每一个中断向量建立了一个中断线程,具体就是在结构中断请求中增加了一个任务结构来代表这个线程:struct IRQ _ desc { IRQ _ flow _ handler _ thandle _ IRQ;structirq _ chip * chip结构desc *结构desc;void * handler _ datavoid * chip _ data structure IRQ ActIon * ActIon;unsignedstatus;unsigned depth;unsigned wake _ depth;unsignedinetirq _ count;unsignedtirqs _ unhandled;unsignedlonglast _ unhandled countstruct task _ struct *线程;//中断线程wait _ queue _ head _ twait _ for _ handler;cycles _ ttimestampraw _ spin lock _ tlockcontchar * name }在中断产生的时候,还是和往常一样进入do_IRQ,这个函数几乎没有什么变化,在do_IRQ中调用了中断请求的handle_irq函数,这个handle_irq是向量相关的,比如有边缘触发等等,这个方式涉及到了硬件规程,故不深入讨论,实际上,每个总线邦定到一个中断向量,而总线的中断方式是总线相关的所以中断向量的方式也就和硬件相关了,这里就以级别类型为例来说明,级别类型的handle_irq是handle _ level _ IRQ:void handle _ level _ IRQ(unsignedintirq,structuriq _ desc * desc){ unsignedintcpu=SMP _ processor _ id();structuriqaction * actionirqreturn _ tacticon _ ret自旋锁(desc锁);mask_ack_irq(desc,IRQ);//屏蔽该中断,以防重入如果(不太可能(desc-statussirq _ INPRESS))//如果正在处理则返回gotoout _ unlockdesc-状态=~(IRQ _ REPLAY | IRQ _ WAITING);中央处理器.IRQ[IRQ];行动=desc-行动;如果(不太可能(!action | |(desc-statussirq _ DISABLE))//禁用则返回gotoout _ unlockdesc-状态|=IRQ _ INPRESS//标记为正在处理if(redirect _ hardiq(desc))//检测是否为线程化中断,若是则唤醒中断线程gotoout _ unlock自旋解锁(desc锁定);action_ret=handle_IRQ_event(irq, action); //非线程化中断,处理之          if (!noirqdebug)                  note_interrupt(irq, desc, action_ret);          spin_lock(&desc->lock);          desc->status &= ~IRQ_INPROGRESS;          if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)                  desc->chip->unmask(irq);  out_unlock:          spin_unlock(&desc->lock); }我们看看每个中断向量的中断线程是怎么初始化的,初始化的细节可以带给我们一大部分必要的信息,在实际开发中一定注意这一点,一个好的初始化带来的是将来操作的方便与清晰: void __init init_hardirqs(void) {          int i;          ok_to_create_irq_threads = 1;          for (i = 0; i < NR_IRQS; i++) {  //对于每一个中断向量建立一个中断线程                  irq_desc_t *desc = irq_desc + i;                  if (desc->action && !(desc->status & IRQ_NODELAY)) //有IRQ_NODELAY标志的中断不允许线程化                          start_irq_thread(i, desc);//实际建立线程          } } static int start_irq_thread(int irq, struct irq_desc *desc) {          if (desc->thread || !ok_to_create_irq_threads)                  return 0;          desc->thread = kthread_create(do_irqd, desc, "IRQ-%d", irq);//建立一个内核线程,赋值给desc->thread。          if (!desc->thread) {                  return -ENOMEM;          }          smp_mb();          wake_up_process(desc->thread); //一切就绪之前即desc->thread被赋值之前可能已经有了中断,故唤醒该中断线程处理之。          return 0; } static int do_irqd(void * __desc) {          struct sched_param param = { 0, };          struct irq_desc *desc = __desc;          current->flags |= PF_NOFREEZE | PF_HARDIRQ;          param.sched_priority = MAX_USER_RT_PRIO/2;          sys_sched_setscheduler(current->pid, SCHED_FIFO, &param); //设置实时优先级          while (!kthread_should_stop()) {                  local_irq_disable_nort();                  do {                          set_current_state(TASK_INTERRUPTIBLE);//一定设置这个标志,否则很少有进程可以抢占中断线程,毕竟它是实时线程,如果不设这个标志,即使下面它自己schedule了,那么很大的可能性还是会选中它的                          do_hardirq(desc);    //处理中断请求                  } while (current->state == TASK_RUNNING);                  local_irq_enable_nort();                  schedule();  //一轮处理完毕后切换到别的进程,实际上除了实时进程可能抢占中断线程,中断线程被强占的可能性极小,因此要主动切出。          }          __set_current_state(TASK_RUNNING);          return 0; } static void do_hardirq(struct irq_desc *desc) {          unsigned long flags;          spin_lock_irqsave(&desc->lock, flags);          if (!(desc->status & IRQ_INPROGRESS))                  goto out; ...          else if (desc->handle_irq == handle_level_irq)                  thread_level_irq(desc); ...          else if (desc->handle_irq == handle_edge_irq)                  thread_edge_irq(desc);          else                  thread_do_irq(desc);   out:          spin_unlock_irqrestore(&desc->lock, flags);          if (waitqueue_active(&desc->wait_for_handler))                  wake_up(&desc->wait_for_handler); }最终在thread_XX_irq中调用handle_IRQ_event来实际处理中断请求。当该中断向量上没有中断要处理的时候,对应的中断线程就主动切出了,而中断来临的时候redirect_hardirq会wakeup对应向量上的中断线程。   现在我们来看看linux中断线程化的意义,传统的linux内核上,中断都是作为最高优先级的执行绪存在的,它实际上并没有什么软件优先级的概念,而是 硬件架构决定了硬件中断到来的时候在该中断没有被屏蔽的条件下必须处理,即便是linux中最高优先级的实时进程也要向中断让路,这就大大削弱了 linux的实时性能,一个实时任务正在运行,将一直被中断打断,特别是在网络负载大的时候,虽然linux将耗时的操作都置于软中断,但是毕竟哪怕很小 延时的硬件中断也要延时,多个小的延时积累起来可能会有很大的延时,这不是实时操作所希望的,于是就有了一种想法,能否让中断也参与到优先级排队,于是中 断线程化就是必然结果了。可是即便大操作放到了线程中,但是毕竟像common_interrupt和do_IRQ还是没有在线程中进行,因此还是会有中 断打断实时任务,linux的这种中断线程化实现仅仅将实时任务被硬件打断的延时降低到了很低的程度,实时任务被中断打断本身并没有得到改善,改善这个境 地的一个方案就是引入硬件中断优先级并和线程优先级联系,在处理实时任务时非使能屏蔽掉不相关的任何硬件中断,使它们不再发生,就像solaris那样, 当然这可以利用硬件的特性,引入“cpu当前优先级”的概念,当cpu处于优先级p时,任何低于p的中断都不能发生,cpu当前优先级和线程优先级以及中 断优先级直接关联,关于这个方案的实现可以参考windows的IRQL或者solaris的IPL。
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。