第10章:中断处理-1: Installing an Interrupt Handler

Linux中断处理与驱动开发

In continuation of the previous text第10章:中断处理: Interrupt Handling let's GO ahead.

Installing an Interrupt Handler

If you want to actually “see” interrupts being generated, writing to the hardware device isn’t enough; a software handler must be configured in the system. If the Linux kernel hasn’t been told to expect your interrupt, it simply acknowledges and ignores it.

若想实际 “看到” 中断被触发,仅向硬件设备写入数据是不够的 —— 还需在系统中配置软件处理程序。如果 Linux 内核未被告知要监听你的中断,它只会简单地确认并忽略该中断。

Interrupt lines are a precious and often limited resource, particularly when there are only 15 or 16 of them. The kernel keeps a registry of interrupt lines, similar to the registry of I/O ports. A module is expected to request an interrupt channel (or IRQ, for interrupt request) before using it and to release it when finished. In many situations, modules are also expected to be able to share interrupt lines with other drivers, as we will see. The following functions, declared in , implement the interrupt registration interface:

中断线是宝贵且通常有限的资源,尤其是当系统只有 15 或 16 条中断线时。内核维护着一个中断线注册表,类似 I/O 端口的注册表。模块在使用中断通道(即中断请求 IRQ)前需先申请,使用完毕后释放。在很多情况下,模块还需能与其他驱动共享中断线(后文会详细说明)。以下函数(声明于<linux/interrupt.h>)实现了中断注册接口:

int request_irq(unsigned int irq,
     irqreturn_t (*handler)(int, void *, struct pt_regs *),
     unsigned long flags,
     const char *dev_name,
     void *dev_id);

void free_irq(unsigned int irq, void *dev_id);

The value returned from request_irq to the requesting function is either 0 to indicate success or a negative error code, as usual. It’s not uncommon for the function to return -EBUSY to signal that another driver is already using the requested interrupt line. The arguments to the functions are as follows:

request_irq 的返回值与常规函数一致:0 表示申请成功,负值表示错误码。其中 -EBUSY 是常见错误,代表请求的中断线已被其他驱动占用。各参数含义如下:

argumentsdescriptions
unsigned int irqThe interrupt number being requested.
irqreturn_t (*handler)(int, void *, struct pt_regs *)The pointer to the handling function being installed. We discuss the arguments to this function and its return value later in this chapter.
unsigned long flagsAs you might expect, a bit mask of options (described later) related to interrupt management.
const char *dev_nameThe string passed to request_irq is used in /proc/interrupts to show the owner of the interrupt (see the next section).
void *dev_id

Pointer used for shared interrupt lines. It is a unique identifier that is used when the interrupt line is freed and that may also be used by the driver to point to its own private data area (to identify which device is interrupting). If the interrupt is not shared, dev_id can be set to NULL, but it a good idea anyway to use this item to point to the device structure. We’ll see a

practical use for dev_id in the section “Implementing a Handler.

  • irq:要申请的中断号;

  • handler:中断处理函数指针;

  • flags:中断标志(如IRQF_SHARED表示共享中断);

  • dev_name:设备名(显示在/proc/interrupts中);

  • dev_id:用于共享中断的设备标识(释放时需匹配)。

The bits that can be set in flags are as follows:

flagsdescritions
SA_INTERRUPTWhen set, this indicates a “fast” interrupt handler. Fast handlers are executed with interrupts disabled on the current processor (the topic is covered in the section “Fast and Slow Handlers”).
SA_SHIRQThis bit signals that the interrupt can be shared between devices. The concept of sharing is outlined in the section “Interrupt Sharing.”
SA_SAMPLE_RANDOM

This bit indicates that the generated interrupts can contribute to the entropy pool used by /dev/random and /dev/urandom. These devices return truly random numbers when read and are designed to help application software choose secure keys for encryption. Such random numbers are extracted from an entropy pool that is contributed by various random events. If your device generates interrupts at truly random times, you should set this flag. If, on the other hand, your interrupts are predictable (for example, vertical blanking of a frame grabber), the flag is not worth setting—it wouldn’t contribute to system entropy anyway. Devices that could be influenced by attackers should not set this flag; for example, network drivers can be subjected to predictable packet timing from outside and should not contribute to the entropy pool. See the comments in

drivers/char/random.c for more information.

标志位作用适用场景
SA_INTERRUPT标记为 “快速中断处理程序”,执行时会禁用当前 CPU 的中断处理时间极短、无需响应其他中断的场景(如简单的硬件状态读取)
SA_SHIRQ允许中断线被多个设备共享系统中断资源紧张,多个设备需复用同一中断号(如多个 USB 设备)
SA_SAMPLE_RANDOM中断事件可贡献到系统熵池(供 /dev/random//dev/urandom 生成随机数)

中断触发时机真正随机的设备(如键盘、鼠标);

不适用:触发时机可预测的设备(如帧抓取器的垂直同步)或易受攻击的设备(如网卡)

The interrupt handler can be installed either at driver initialization or when the device is first opened. Although installing the interrupt handler from within the module’s initialization function might sound like a good idea, it often isn’t, especially if your device does not share interrupts. Because the number of interrupt lines is limited, you don’t want to waste them. You can easily end up with more devices in your computer than there are interrupts. If a module requests an IRQ at initialization, it prevents any other driver from using the interrupt, even if the device holding it is never used. Requesting the interrupt at device open, on the other hand, allows some sharing of resources.

中断处理程序可以在驱动初始化时安装,也可以在设备首次打开时安装。尽管在模块初始化函数中安装中断处理程序听起来是个好主意,但通常并不可取 —— 尤其是当设备不支持中断共享时。由于中断线数量有限,不应浪费它们。计算机中的设备数量很容易超过可用中断数,如果模块在初始化时就申请 IRQ,会阻止其他驱动使用该中断,即便持有该中断的设备从未被使用过。而在设备打开时申请中断,则可以实现资源的共享。

It is possible, for example, to run a frame grabber on the same interrupt as a modem, as long as you don’t use the two devices at the same time. It is quite common for users to load the module for a special device at system boot, even if the device is rarely used. A data acquisition gadget might use the same interrupt as the second serial port. While it’s not too hard to avoid connecting to your Internet service provider (ISP) during data acquisition, being forced to unload a module in order to use the modem is really unpleasant.

例如,只要不同时使用帧捕获器和调制解调器,它们就可以共用同一个中断。用户常常在系统启动时加载专用设备的模块,即便该设备很少使用。数据采集设备可能与第二个串口使用同一个中断:在数据采集时避免连接互联网服务提供商(ISP)并不难,但为了使用调制解调器而被迫卸载模块则非常麻烦。

The correct place to call request_irq is when the device is first opened, before the hardware is instructed to generate interrupts. The place to call free_irq is the last time the device is closed, after the hardware is told not to interrupt the processor any more. The disadvantage of this technique is that you need to keep a per-device open count so that you know when interrupts can be disabled.

调用 request_irq 的正确时机是设备首次打开时(在指示硬件生成中断之前);而调用 free_irq 则应在设备最后一次关闭时(在告知硬件停止中断处理器之后)。这种方式的缺点是需要维护一个设备打开计数,以确定何时可以禁用中断。

This discussion notwithstanding, short requests its interrupt line at load time. This was done so that you can run the test programs without having to run an extra process to keep the device open. short, therefore, requests the interrupt from within its initialization function (short_init) instead of doing it in short_open, as a real device driver would.

尽管如此,short 模块还是在加载时申请了中断线。这样做是为了让测试程序无需额外进程保持设备打开状态即可运行。因此,short 在其初始化函数(short_init)中申请中断,而非像真实设备驱动那样在 short_open 中处理。

The interrupt requested by the following code is short_irq. The actual assignment of the variable (i.e., determining which IRQ to use) is shown later, since it is not relevant to the current discussion. short_base is the base I/O address of the parallel interface being used; register 2 of the interface is written to enable interrupt reporting.

以下代码申请的中断是 short_irq(变量的具体赋值 —— 即确定使用哪个 IRQ—— 将在后面展示,与当前讨论无关)。short_base 是所用并行接口的基地址,通过写入接口的 2 号寄存器启用中断报告:

if (short_irq >= 0) {
 result = request_irq(short_irq, short_interrupt,
 SA_INTERRUPT, "short", NULL);
 if (result) {
     printk(KERN_INFO "short: can't get assigned irq %i\n",
     short_irq);
     short_irq = -1;
 }
 else { /* actually enable it -- assume this *is* a parallel port */
     outb(0x10,short_base+2);
 }
}

The code shows that the handler being installed is a fast handler (SA_INTERRUPT), doesn’t support interrupt sharing (SA_SHIRQ is missing), and doesn’t contribute to system entropy (SA_SAMPLE_RANDOM is missing, too). The outb call then enables interrupt reporting for the parallel port.

For what it’s worth, the i386 and x86_64 architectures define a function for querying the availability of an interrupt line:

代码表明,安装的是快速处理程序SA_INTERRUPT 标志),不支持中断共享(缺少 SA_SHIRQ),也不参与系统熵值生成(也缺少 SA_SAMPLE_RANDOM)。随后的 outb 调用用于启用并行端口的中断报告功能。

值得一提的是,i386 和 x86_64 架构定义了一个查询中断线可用性的函数:

int can_request_irq(unsigned int irq, unsigned long flags);

This function returns a nonzero value if an attempt to allocate the given interrupt succeeds. Note, however, that things can always change between calls to can_request_irq and request_irq.

若分配指定中断的尝试可能成功,该函数返回非零值。但需注意,在 can_request_irq 和 request_irq 的调用之间,情况可能发生变化(因此不能依赖该函数的返回值作为最终结果)。

补充说明:

  1. SA_INTERRUPT 标志的含义

    该标志表示中断处理程序是 “快速处理程序”,运行时会禁用所有其他中断(仅在旧内核中有效,现代内核已弃用此标志)。快速处理程序应尽可能简短,适合处理对延迟敏感的中断。

  2. 中断申请时机的权衡

    申请时机优点缺点
    模块初始化时无需维护打开计数,测试方便可能长期占用中断线,浪费资源
    设备首次打开时按需申请,提高中断线利用率需管理打开计数,逻辑更复杂
  3. 并行端口中断启用的硬件细节

    ​​​​​​​outb(0x10, short_base + 2) 向并行端口的控制寄存器(基地址 + 2)写入 0x10,该值对应 “允许中断” 位(第 4 位),硬件收到后会开始通过 IRQ 线发送中断信号。

  4. 共享中断的前提

    ​​​​​​​若要支持中断共享,需满足:

    1. 中断处理程序需能判断中断是否由自己的设备触发(避免误处理)。

    2. dev_id 不能为 NULL(用于区分共享同一中断的不同设备);

    3. 申请时指定 SA_SHIRQ 标志;

-> Installing FirebaseAnalytics (11.8.0) > Http download $ /usr/bin/curl -f -L -o /var/folders/kr/xtwcw1w52g957t5l5wmft2xc0000gn/T/d20250725-74885-h8rk2/file.tgz https://dl.google.com/firebase/ios/analytics/f18d9810c6c5311c/FirebaseAnalytics-11.7.0.tar.gz --create-dirs --netrc-optional --retry 2 -A 'CocoaPods/1.16.2 cocoapods-downloader/2.1' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:03 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:04 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:05 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:06 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:07 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:08 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:09 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:10 --:--:-- 0 0 0 0 0 0 0 0 --:--:-- 0:00:10 --:--:-- 0 curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to dl.google.com:443 [!] Error installing FirebaseAnalytics [!] /usr/bin/curl -f -L -o /var/folders/kr/xtwcw1w52g957t5l5wmft2xc0000gn/T/d20250725-74885-h8rk2/file.tgz https://dl.google.com/firebase/ios/analytics/f18d9810c6c5311c/FirebaseAnalytics-11.7.0.tar.gz --create-dirs --netrc-optional --retry 2 -A 'CocoaPods/1.16.2 cocoapods-downloader/2.1' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- 0:00:10 --:--:-- 0 curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to dl.google.com:443
07-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DeeplyMind

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

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

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

打赏作者

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

抵扣说明:

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

余额充值