详解Linux中断处理中的hardirq与softirq机制

转载 2012年03月22日 16:30:14

今天在ChinaUnix论坛内核源码版上与linuxfellow网友讨论到hardirq和softirq的问题,虽然在《深入Linux设备驱动程序内核机制》(以下简称“ILDD”)第5章“中断处理”对此已有详细的解读,但是我觉得还是有必要再花点时间深入探讨一下这两者的区别。因为此前关于ARM上的中断处理我已经在另一篇帖子解密ARM based Linux内核中断处理框架 中讨论过,所以下面的讨论只限于x86 32位系统。

首先给出一个ILDD书中的一个插图,关于中断处理的整体框架:




(图1)

接下来的一个插图显示了内核用于标识中断上下文(in_interrupt())的变量preempt_count的布局:


(图2)

按照x86处理器在外部中断发生时的硬件逻辑,在do_IRQ被调用时,处理器已经屏蔽了对外部中断的响应。在图中我们看到中断的处理大体上被分成两部分HARDIRQ和SOFTIRQ,对应到代码层面,do_IRQ()中调用irq_enter函数可以看做hardirq 部分的开始,而irq_exit函数的调用则标志着softirq部分的开始:

<arch/x86/kernel/irq.c>


  1. unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
  2. {
  3.         ...
  4.         irq_enter();

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

  8.         return 1;
  9. }

irq_enter()函数的核心调用是__irq_enter(),后者的主要作用是在图2的 preempt_count变量的HARDIRQ部分+1,即标识一个hardirq的上下文,所以可以认为do_IRQ()调用irq_enter函数意味着中断处理进入hardirq阶段。此时处理器响应外部中断的能力依然是被disable掉的(EFLAG.IF=0),因为ISR基本上属于设备驱动程序涉足的领域,内核无法保证在设备驱动程序的ISR中会否将EFLAG.IF置1(此处涉及可能的中断嵌套以及中断栈溢出问题,可以参考ARM中断处理的那个帖子),所以我们会在内核代码中看到内核开发者为了尽可能避免非受控的ISR部分给系统带来的损害所做的努力。按照现在内核的设计理念,在 hardirq部分最好不要打开中断,所以处在hardirq上下文中的设备驱动程序的ISR应该尽可能快地返回(所谓只完成最关键的任务),而将耗时的中断处理善后工作留到softirq部分,因为在softirq部分,内核会使EFLAG.IF=1,所以也意味着softirq部分可以随时被外部中断所打断。

下面我们重点讨论一下softirq部分的实现原理,do_IRQ()中调用irq_exit函数标志softirq部分的开始。 irq_exit()函数会首先在图2的preempt_count变量的HARDIRQ部分-1,目的是清除hardirq的上下文标记。然后它有个关键的调用invoke_softirq,用于实际的softirq处理工作:

<kernel/softirq.c>


  1. void irq_exit(void)
  2. {
  3.         ...
  4.         sub_preempt_count(IRQ_EXIT_OFFSET);
  5.         if (!in_interrupt() && local_softirq_pending())
  6.                 invoke_softirq();

  7.         rcu_irq_exit();
  8.         ...
  9. }
ILDD中对in_interrupt()函数的叙述很明确:"...其主要用意是根据当前preempt_count变量,来判断当前代码是否在一个中断上下文中执行。根据in_interrupt的定义来看,Linux内核认为HARDIRQ、SOFTIRQ以及NMI 都属于interrupt范畴...",所以softirq部分是否被执行,取决于:1.当前是否在中断上下文,2. 是否有pending的softirq需要处理。
第一个条件,主要用来防止softirq部分的重入,因为一旦有pending的softirq需要处理,那么invoke_softirq()的调用 (实际的工作发生在__do_softirq函数中)首先会将图2中SOFTIRQ部分+1,这样若在当前正在处理softirq过程中发生了外部中断,hardirq部分标识了一个pending softirq,那么在irq_exit函数中将直接返回,而不会调用到invoke_softirq。

softirq部分的核心代码段如下:

<kernel/softirq.c>
  1. asmlinkage void __do_softirq(void)
  2. {
  3.         ...
  4.         //获得__softirq_pending变量上保存的pending softirq数据
  5.         pending = local_softirq_pending();
  6.         //将图2中SOFTIRQ部分+1,标识softirq上下文,此时in_interrupt()返回true.
  7.         __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
  8.         ...
  9. restart:
  10.         /* Reset the pending bitmask before enabling irqs */
  11.         set_softirq_pending(0);
  12.         //注意此处,内核调用local_irq_enable打开处理器响应外部中断能力,EFLAG.IF=1,所以接下来的softirq处理时外部中断可以进入处理器
  13.         local_irq_enable();

  14.         h = softirq_vec;
  15.         do {
  16.                 if (pending & 1) {
  17.                         ...
  18.                         //调用每个类型的softirq所对应的处理函数,见下图3
  19.                         h->action(h);
  20.                         ...
  21.                 }
  22.                 h++;
  23.                 pending >>= 1;
  24.         } while (pending);
  25.         //softirq处理结束前,重新关闭中断。中断的再次打开发生在中断的返回,也就是在图1中处理器在执行iret指令时的硬件逻辑,POP EFLAG, EFLAG.IF=1
  26.         local_irq_disable();
  27.        
  28.         //再次检测是否有新的pending softirq,因为softirq执行时可能有新的外部中断进来,如果有,此处一并处理。
  29.         pending = local_softirq_pending();
  30.         if (pending && --max_restart)
  31.                 goto restart;
  32.         ...
  33.         //将图2中SOFTIRQ部分-1,标识softirq上下文的结束
  34.         __local_bh_enable(SOFTIRQ_OFFSET);
  35. }
其中每个关键的节点都做了注释,ILDD书中对此也有详细的说明,包括其中的ksoftirqd的用法。

对每个类型的softirq的处理发生在上面的do...while...循环中,原理其实非常简单,为节省文字,用下面一个草图来做说明(图3):



所以do...while...循环实际上是从bit 0遍历__softirq_pending变量,目前该变量的0~9分别对应10个不同类型的softirq,每个softirq对应不同的处理函数,比如HI_SOFTIRQ对应的action为tasklet_hi_action,它与TASKLET_SOFTIRQ的action的原理完全一样,也就是我们平常所说的tasklet。__softirq_pending是个per-CPU型的变量,因为SMP系统中每个处理器都可以独立处理各自到来的外部中断,也都对应有各自的hardirq栈和softirq栈,详见Linux内核中的中断栈与内核栈的补充说明一贴。另外,在前面的帖子中我们一直说hardirq部分标识一个softirq,何谓标识一个softirq?其实就是调用tasklet_schedule()来把TASKLET_SOFTIRQ位置1.

在ILDD一书中实际上将tasklet分成两部分,分别放在第5章”中断处理“和第6章”延迟操作“,第5章侧重讨论softirq的机制,而第6章则重点讨论tasklet。

相关文章推荐

浅谈linux2.4 内核中断下半部分(softirq机制)

linux2.4 内核中断下半部分(bottom half)理解(请结合linux2.4内核代码看): 首先先说一下为什么要采用中断下半部分: 中断服务函数大多需要在关中断的情况下去执行,但是有的...

Linux内核中断机制(三):中断处理上

内核中断处理过程 S3C2410和Linux2.6.26内核为例讲解处理过程   1.中断向量表arch\arm\kernel\entry-armv.S __vectors_start: s...

ARM Linux中断机制之中断处理

//现在来看看中断初始化的另一个函数early_trap_init(),该函数在文件arch/arm/kernel/traps.c中实现。 void __init early_trap_init...

linux内核架构-中断处理机制

本文对中断系统进行了全面的分析与探讨,主要包括中断控制器、中断分类、中断亲和力、中断线程化与 SMP 中的中断迁徙等。首先对中断工作原理进行了简要分析,接着详细探讨了中断亲和力的实现原理,最后对中断线...

Linux 中断处理的小任务(tasklet)机制 简介

中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。但是,中断是一个随机事件,它随时会到来,如果关中断的时间太长,CPU就不能及时响应其他的中断请求,从而造成中断的丢失。因...

Linux内核中断机制(四):中断处理下

中断处理模型 要想弄清楚 desc->handle_irq(irq, desc)和我们注册的中断有什么关联,就要 了解中断处理模型了。   1.中断处理模型结构   ①NR_IRQS:最大...

ARM_Linux中断处理实时性能分析.pdf

  • 2008年06月06日 18:05
  • 186KB
  • 下载

Linux内核的Softirq机制

第六章 Linux内核的Softirq机制 (By 詹荣开,NUDT)     Copyright © 2003 by 詹荣开 E-mail:zhanrk@sohu.com ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:详解Linux中断处理中的hardirq与softirq机制
举报原因:
原因补充:

(最多只允许输入30个字)