第10章:中断处理-5:Fast and Slow Handlers

In continuation of the previous text 第10章:中断处理-4: Autodetecting the IRQ Number--DIY probing let's GO ahead.

Fast and Slow Handlers

Fast and Slow Handlers Older versions of the Linux kernel took great pains to distinguish between “fast” and “slow” interrupts. Fast interrupts were those that could be handled very quickly, whereas handling slow interrupts tooksignificantly longer. Slow interrupts could be sufficiently demanding of the processor, and it was worthwhile to reenable interrupts while they were being handled. Otherwise, tasks requiring quick attention could be delayed for too long.

早期的 Linux 内核会刻意区分 “快速” 和 “慢速” 中断。快速中断指那些能被极快处理的中断,而处理慢速中断则需要显著更长的时间。慢速中断对处理器的占用可能足够高,因此在处理它们时重新启用其他中断是值得的 —— 否则,需要快速响应的任务可能会被延迟过久。

In modern kernels, most of the differences between fast and slow interrupts have disappeared. There remains only one: fast interrupts (those that were requested with the SA_INTERRUPT flag) are executed with all other interrupts disabled on the current processor. Note that other processors can still handle interrupts, although you will never see two processors handling the same IRQ at the same time.

在现代内核中,快速中断与慢速中断之间的大多数差异已消失,仅剩下一点区别:快速中断(通过 SA_INTERRUPT 标志申请的中断)在当前处理器上执行时,会禁用所有其他中断。需注意,其他处理器仍可处理中断,且你永远不会看到两个处理器同时处理同一个 IRQ。

So, which type of interrupt should your driver use? On modern systems, SA_INTERRUPT is intended only for use in a few, specific situations such as timer interrupts. Unless you have a strong reason to run your interrupt handler with other interrupts disabled, you should not use SA_INTERRUPT.

那么,驱动应使用哪种类型的中断呢?在现代系统中,SA_INTERRUPT 仅用于少数特定场景(如定时器中断)。除非有充分理由让中断处理程序在禁用其他中断的情况下运行,否则不应使用 SA_INTERRUPT

This description should satisfy most readers, although someone with a taste for hardware and some experience with her computer might be interested in going deeper. If you don’t care about the internal details, you can skip to the next section.

以上描述足以满足大多数读者的需求,但对硬件感兴趣或有计算机使用经验的人可能希望深入了解更多细节。如果你不关心内部细节,可以跳至下一节。

补充说明:

  1. 早期内核中 “快慢” 中断的核心差异

    在 2.4 及更早内核中,快慢中断的区别更显著:
    1. 慢速中断:仅禁用当前 IRQ 线,允许其他中断嵌套,可执行稍复杂的处理(如数据拷贝)。现代内核通过 “中断线程化”(将部分处理移至内核线程)弱化了这种区分,SA_INTERRUPT 的作用仅剩 “禁用当前 CPU 所有中断”。

    2. 快速中断:禁用所有中断,处理逻辑必须极简(如仅清除中断标志),不能睡眠、不能调用复杂函数;

  2. SA_INTERRUPT 的潜在风险

    使用该标志可能导致严重问题:
    1. 现代硬件(如多核 CPU、高速外设)对中断延迟更敏感,SA_INTERRUPT 的适用场景已极少。

    2. 若处理程序执行时间过长,会阻塞所有其他中断,甚至导致系统无响应;

    3. 禁用所有中断会延长系统的 “中断延迟”,可能错过对时间敏感的事件(如实时任务、硬件定时器);

  3. 现代内核的替代方案

    如需优化中断处理,现代内核更推荐以下方式,而非使用 SA_INTERRUPT
    1. 避免长耗时操作:将数据处理、内存分配等耗时操作移至下半部,缩短上半部执行时间。

    2. 优先级调度:为关键中断(如定时器)设置更高优先级,确保其优先响应;

    3. 中断线程化:通过 request_threaded_irq 将中断分为 “上半部”(快速处理硬件信号,禁用中断)和 “下半部”(在进程上下文处理复杂逻辑,可睡眠);

  4. 适用 SA_INTERRUPT 的特殊场景

    仅在以下极少数场景中可能需要该标志:
    1. 与硬件时序强相关的操作(如某些老式设备的寄存器读写),需保证操作的原子性。

    2. 处理极短的硬件信号(如脉冲信号),需避免被其他中断打断导致信号丢失;

The internals of interrupt handling on the x86

This description has been extrapolated from arch/i386/kernel/irq.c, arch/i386/kernel/ apic.c, arch/i386/kernel/entry.S, arch/i386/kernel/i8259.c, and include/asm-i386/hw_irq.h as they appear in the 2.6 kernels; although the general concepts remain the same, the hardware details differ on other platforms.

以下内容基于 2.6 内核中的 arch/i386/kernel/irq.carch/i386/kernel/apic.carch/i386/kernel/entry.Sarch/i386/kernel/i8259.c 以及 include/asm-i386/hw_irq.h 推导得出;尽管核心概念保持一致,但其他平台的硬件细节会有所不同。

The lowest level of interrupt handling can be found in entry.S, an assembly-language file that handles much of the machine-level work. By way of a bit of assembler trickery and some macros, a bit of code is assigned to every possible interrupt. In each case, the code pushes the interrupt number on the stackand jumps to a common segment, which calls do_IRQ, defined in irq.c.

中断处理的最底层逻辑位于汇编文件 entry.S 中,该文件负责处理大量机器级操作。通过一些汇编技巧和宏定义,系统会为每个可能的中断分配一段代码:在每种情况下,代码会将中断号压入栈中,然后跳转到一个公共代码段,该代码段会调用 irq.c 中定义的 do_IRQ 函数。

The first thing do_IRQ does is to acknowledge the interrupt so that the interrupt controller can go on to other things. It then obtains a spinlockfor the given IRQ number, thus preventing any other CPU from handling this IRQ. It clears a couple of status bits (including one called IRQ_WAITING that we’ll lookat shortly) and then looks up the handler(s) for this particular IRQ. If there is no handler, there’s nothing to do; the spinlockis released, any pending software interrupts are handled, and do_IRQ returns.

do_IRQ 首先会确认中断(acknowledge the interrupt),让中断控制器可以继续处理其他中断。随后,它会为当前 IRQ 号获取一个自旋锁(spinlock),防止其他 CPU 同时处理该 IRQ。接着,do_IRQ 会清除几个状态位(包括一个名为 IRQ_WAITING 的位,后续会介绍),然后查找该 IRQ 对应的处理程序(handler)。若没有注册处理程序,则无需进一步操作:释放自旋锁、处理所有待处理的软中断,最后 do_IRQ 返回。

Usually, however, if a device is interrupting, there is at least one handler registered for its IRQ as well. The function handle_IRQ_event is called to actually invoke the handlers. If the handler is of the slow variety (SA_INTERRUPT is not set), interrupts are reenabled in the hardware, and the handler is invoked. Then it’s just a matter of cleaning up, running software interrupts, and getting backto regular work. The “regular work” may well have changed as a result of an interrupt (the handler could wake_up a process, for example), so the last thing that happens on return from an interrupt is a possible rescheduling of the processor.

Probing for IRQs is done by setting the IRQ_WAITING status bit for each IRQ that currently lacks a handler. When the interrupt happens, do_IRQ clears that bit and then returns, because no handler is registered. probe_irq_off, when called by a driver, needs to search for only the IRQ that no longer has IRQ_WAITING set.

不过,通常情况下,若设备触发中断,该 IRQ 至少会注册一个处理程序。此时会调用 handle_IRQ_event 函数来实际执行这些处理程序:

  • 若处理程序是慢速类型(未设置 SA_INTERRUPT 标志),硬件会重新启用中断,随后执行处理程序;
  • 处理完成后,只需进行清理工作(如运行软中断),再回到正常任务执行流程。

由于中断可能会改变 “正常任务” 的状态(例如,处理程序可能通过 wake_up 唤醒某个进程),因此从中断返回前的最后一步操作,通常是判断是否需要重新调度处理器(rescheduling)。

IRQ 探测功能通过为当前无处理程序的 IRQ 设置 IRQ_WAITING 状态位实现:当中断发生时,do_IRQ 会清除该位,然后因无注册处理程序而返回。当驱动调用 probe_irq_off 时,只需查找不再设置 IRQ_WAITING 位的 IRQ,即可确定探测到的中断号。

补充说明:

  1. 中断确认(Acknowledge)的硬件意义

    ​​​​​​​中断确认是向中断控制器(如 8259A 或 APIC)发送信号,告知 “中断已被接收,可接收下一个中断”。若不确认,中断控制器会持续保持中断请求状态,导致处理器反复响应同一中断。
    1. 对于 APIC 控制器:需通过内存映射寄存器清除中断请求标志。

    2. 对于 8259A 控制器:需向特定端口写入确认命令(如 0x20);

  2. 自旋锁在中断处理中的作用

    ​​​​​​​do_IRQ 获取的自旋锁(通常是 irq_desc[irq].lock)用于保证同一 IRQ 在多 CPU 间的互斥处理
    1. 该锁是 “中断安全” 的,获取时会自动禁用当前 CPU 的本地中断(避免死锁)。

    2. 防止两个 CPU 同时执行同一 IRQ 的处理程序,避免共享资源竞争;

  3. IRQ_WAITING 位的探测逻辑细节

    ​​​​​​​​​​​​​​探测过程中,probe_irq_on 会遍历所有无处理程序的 IRQ,为其设置 IRQ_WAITING 位;当设备触发中断后,do_IRQ 会清除该 IRQ 的 IRQ_WAITING 位。probe_irq_off 只需对比探测前后的 IRQ_WAITING 状态,即可定位到 “被触发的 IRQ”—— 这也是内核辅助探测的核心原理。
  4. 软中断(Software Interrupt)的触发时机

    ​​​​​​​处理完硬件中断后运行软中断(如 taskletworkqueue),是为了将 “耗时操作” 从中断上下文移至软中断上下文:
    1. 常见软中断包括网络数据包处理(NET_TX_SOFTIRQ)、定时器到期处理(TIMER_SOFTIRQ)等。

    2. 软中断可在中断处理完成后、返回用户空间前执行,避免阻塞硬件中断;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值