尽管有些设备仅仅通过控制其寄存器就可以得到控制,但现实中的大部分设备却要比这复杂一些。因为大部分设备的处理时间与处理器不在同一个周期,且一定会比处理器慢的多,这就造成了一种让处理器等待设备的现象,显然这是不行的,而有一种解决方法就是中断操作。
中断仅仅就是一个信号,当硬件需要获得处理器对它的关注时,就可以发送这个信号。 Linux 处理中断的方式非常类似在用户空间处理信号的方式。大多数情况下,一个驱动只需要为它的设备的中断注册一个处理例程,并当中断到来时进行正确的处理。本质上来讲,中断处理例程和其他的代码并行运行。因此,它们不可避免地引起并发问题,并竞争数据结构和硬件。 透彻地理解并发控制技术对中断来讲非常重要。
安装中断处理例程
中断信号线
内核维护了一个中断信号线的注册表,改注册表类似于I/O端口的注册表。模块在使用中断之前要先请求一个中断通道(或者中断请求IRQ),然后在使用后释放该通道。
<linux/sched.h> int request_irq(unsigned long irq, irqreturn_t (*handler)(int,void *,struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id); 表示注册中断,返回值: 0 指示成功,或返回一个负的错误码,如 -EBUSY 表示另一个驱动已经占用了你所请求的中断线。 参数: unsigned int irq:要申请的中断号 irqreturn_t (*handler)(int,void *,struct pt_regs *):要安装的中断处理函数的指针 unsigned long flags:与中断管理相关的位掩码选项 const char *dev_name:传递给request_irq的字符串,用来在/proc/interrupts显示中断的拥有者 void *dev_id:用于共享的中断信号线,它是唯一的标识,在中断线空闲时可以使用它,驱动程序也可以用它来指向自己的私有数据区(来标识哪个设备产生中断)。若中断没有被共享,dev_id 可以设置为 NULL,但推荐用它指向设备的数据结构。 flags: SA_INTERRUPT :快速中断标志。快速中断处理例程运行在当前处理器禁止中断的状态下。 SA_SHIRQ : 在设备间共享中断标志。 SA_SAMPLE_RANDOM :该位表示产生的中断能对 /dev/random 和 /dev/urandom 使用的熵池(entropy pool)有贡献。 读取这些设备会返回真正的随机数,从而有助于应用程序软件选择用于加密的安全密钥。 若设备以真正随机的周期产生中断,就应当设置这个标志。若设备中断是可预测的,这个标志不值得设置。可能被攻击者影响的设备不应当设置这个标志。更多信息看 drivers/char/random.c 的注释。void free_irq(unsigned int irq,void *dev_id); 释放中断 |
中断处理例程可在驱动初始化时或在设备第一次打开时安装。推荐在设备第一次打开、硬件被告知产生中断前时申请中断,因为可以共享有限的中断资源。这样调用 free_irq 的位置是设备最后一次被关闭、硬件被告知不用再中断处理器之后。但这种方式的缺点是必须为每个设备维护一个打开计数。
以下是中断申请的示例(并口):
|
i386 和 x86_64 体系定义了一个函数来查询一个中断线是否可用:
|
x86中断处理内幕
这个描述是从 2.6 内核 arch/i386/kernel/irq.c, arch/i386/kernel/ apic.c, arch/i386/kernel/entry.S, arch/i386/kernel/i8259.c, 和 include/asm-i386/hw_irq.h 中得出,尽管基本概念相同,硬件细节与其他平台上不同。
底层中断处理代码在汇编语言文件 entry.S。在所有情况下,这个代码将中断号压栈并且跳转到一个公共段,公共段会调用 do_IRQ(在 irq.c 中定义)。do_IRQ 做的第一件事是应答中断以便中断控制器能够继续其他事情。它接着获取给定 IRQ 号的一个自旋锁,阻止其他 CPU 处理这个 IRQ,然后清除几个状态位(包括IRQ_WAITING )然后查找这个 IRQ 的处理例程。若没有找到,什么也不做;释放自旋锁,处理任何待处理的软件中断,最后 do_IRQ 返回。从中断中返回的最后一件事可能是一次处理器的重新调度。
IRQ的探测是通过为每个缺乏处理例程的IRQ设置 IRQ_WAITING 状态位来完成。当中断发生, 因为没有注册处理例程,do_IRQ 清除这个位并且接着返回。 当probe_irq_off被一个函数调用,只需搜索没有设置 IRQ_WAITING 的 IRQ。
快速和慢速处理例程
快速中断是那些能够很快处理的中断,而处理慢速中断会花费更长的时间。在处理慢速中断时处理器重新使能中断,避免快速中断被延时过长。在现代内核中,快速和慢速中断的区别已经消失,剩下的只有一个:快速中断(使用 SA_INTERRUPT )执行时禁止所有在当前处理器上的其他中断。注意:其他的处理器仍然能够处理中断。
除非你充足的理由在禁止其他中断情况下来运行中断处理例程,否则不应当使用SA_INTERRUPT.
/proc接口
当硬件中断到达处理器时, 内核提供的一个内部计数器会递增,产生的中断报告显示在文件 /proc/interrupts中。这一方法可以用来检查设备是否按预期地工作。此文件只显示当前已安装处理例程的中断的计数。若以前request_irq的一个中断,现在已经free_irq了,那么就不会显示在这个文件中,但是它可以显示终端共享的情况。
/proc/stat记录了几个关于系统活动的底层统计信息, 包括(但不仅限于)自系统启动以来收到的中断数。 stat 的每一行以一个字符串开始, 是该行的关键词:intr 标志是中断计数。第一个数是所有中断的总数, 而其他每一个代表一个单独的中断线的计数, 从中断 0 开始(包括当前没有安装处理例程的中断),无法显示终端共享的情况。
以上两个文件的一个不同是:/proc/interrupts几乎不依赖体系,而/proc/stat的字段数依赖内核下的硬件中断,其定义在<asm/irq.h>中。ARM的定义为:
|
自动检测 IRQ 号
驱动初始化时最迫切的问题之一是决定设备要使用的IRQ 线,驱动需要信息来正确安装处理例程。自动检测中断号对驱动的可用性来说是一个基本需求。有时自动探测依赖一些设备具有的默认特性,以下是典型的并口中断探测程序:
|