linux的中断线程化实现

2.6.25.8内核实现了中断线程化,内核为每一个中断向量建立了一个中断线程,具体就是在结构irq_desc中增加了一个task_struct来代表这个线程:

struct irq_desc {

         irq_flow_handler_t      handle_irq;

         struct irq_chip         *chip;

         struct msi_desc         *msi_desc;

         void                    *handler_data;

         void                    *chip_data;

         struct irqaction        *action;        /* IRQ action list */

         unsigned int            status;         /* IRQ status */

         unsigned int            depth;          /* nested irq disables */

         unsigned int            wake_depth;     /* nested wake enables */

         unsigned int            irq_count;      /* For detecting broken IRQs */

         unsigned int            irqs_unhandled;

         unsigned long           last_unhandled; /* Aging timer for unhandled count */

         struct task_struct      *thread;         //中断线程

         wait_queue_head_t       wait_for_handler;

         cycles_t                timestamp;

         raw_spinlock_t          lock;

         const char              *name;

}

在 中断产生的时候,还是和往常一样进入do_IRQ,这个函数几乎没有什么变化,在do_IRQ中调用了irq_desc的handle_irq函数,这个 handle_irq是向量相关的,比如有边缘触发等等,这个方式涉及到了硬件规程,故不深入讨论,实际上,每个总线邦定到一个中断向量,而总线的中断方式是总线相关的所以中断向量的方式也就和硬件相关了,这里就以Level type为例来说明,Level type的handle_irq是handle_level_irq:

void handle_level_irq(unsigned int irq, struct irq_desc *desc)

{

         unsigned int cpu = smp_processor_id();

         struct irqaction *action;

         irqreturn_t action_ret;

         spin_lock(&desc->lock);

         mask_ack_irq(desc, irq);  //屏蔽该中断,以防重入

         if (unlikely(desc->status & IRQ_INPROGRESS))//如果正在处理则返回

                 goto out_unlock;

         desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);

         kstat_cpu(cpu).irqs[irq]++;

         action = desc->action;

         if (unlikely(!action || (desc->status & IRQ_DISABLED)))//禁用则返回

                 goto out_unlock;

         desc->status |= IRQ_INPROGRESS; //标记为正在处理

         if (redirect_hardirq(desc)) //检测是否为线程化中断,若是则唤醒中断线程

                 goto out_unlock;

         spin_unlock(&desc->lock);

         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。

原文链接: https://blog.csdn.net/dog250/article/details/5303251

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    linux的中断线程化实现

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/410463

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 下午12:02
下一篇 2023年4月26日 下午12:02

相关推荐