如果你想实际地"看到"产生的中断, 向硬件设备写不足够; 一个软件处理必须在系统中配 置. 如果 Linux 内核还没有被告知来期待你的中断, 它简单地确认并忽略它.
中断线是一个宝贵且常常有限的资源, 特别当它们只有 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);
从 request_irq 返回给请求函数的返回值或者是 0 指示成功, 或者是一个负的错误码, 如同平常. 函数返回 -EBUSY 来指示另一个驱动已经使用请求的中断线是不寻常的. 函数 的参数如下:
unsigned int irq
请求的中断号 irqreturn_t (*handler)
安装的处理函数指针. 我们在本章后面讨论给这个函数的参数以及它的返回值. unsigned long flags
如你会希望的, 一个与中断管理相关的选项的位掩码(后面描述). const char *dev_name
这个传递给 request_irq 的字串用在 /proc/interrupts 来显示中断的拥有者(下 一节看到)
void *dev_id
用作共享中断线的指针. 它是一个独特的标识, 用在当释放中断线时以及可能还被 驱动用来指向它自己的私有数据区(来标识哪个设备在中断). 如果中断没有被共享, dev_id 可以设置为 NULL, 但是使用这个项指向设备结构不管如何是个好主意. 我 们将在"实现一个处理"一节中看到 dev_id 的一个实际应用.
flags 中可以设置的位如下:
221
SA_INTERRUPT
当置位了, 这表示一个"快速"中断处理. 快速处理在当前处理器上禁止中断来执行 (这个主题在"快速和慢速处理"一节涉及).
SA_SHIRQ
这个位表示中断可以在设备间共享. 共享的概念在"中断共享"一节中略述.
SA_SAMPLE_RANDOM
这个位表示产生的中断能够有贡献给 /dev/random 和 /dev/urandom 使用的加密 池. 这些设备在读取时返回真正的随机数并且设计来帮助应用程序软件为加密选择 安全钥. 这样的随机数从一个由各种随机事件贡献的加密池中提取的. 如果你的设 备以真正随机的时间产生中断, 你应当设置这个标志. 如果, 另一方面, 你的中断 是可预测的( 例如, 一个帧抓取器的场消隐), 这个标志不值得设置 -- 它无论如 何不会对系统加密有贡献. 可能被攻者影响的设备不应当设置这个标志; 例如, 网络驱动易遭受从外部计时的可预测报文并且不应当对加密池有贡献. 更多信息看 drivers/char/random.c 的注释.
中断处理可以在驱动初始化时安装或者在设备第一次打开时. 尽管从模块的初始化函数中 安装中断处理可能听来是个好主意, 它常常不是, 特别当你的设备不共享中断. 因为中断 线数目是有限的, 你不想浪费它们. 你可以轻易使你的系统中设备数多于中断数.如果一 个模块在初始化时请求一个 IRQ, 它阻止了任何其他的驱动使用这个中断, 甚至这个持有 它的设备从不被使用. 在设备打开时请求中断, 另一方面, 允许某些共享资源.
例如, 可能与一个 modem 在同一个中断上运行一个帧抓取器, 只要你不同时使用这 2 个 设备. 对用户来说是很普通的在系统启动时为一个特殊设备加载模块, 甚至这个设备很少 用到. 一个数据获取技巧可能使用同一个中断作为第 2 个串口. 虽然不是太难避免在数 据获取时联入你的互联网服务提供商(ISP), 被迫卸载一个模块为了使用 modem 确实令人 不快.
调用 request_irq 的正确位置是当设备第一次打开时, 在硬件被指示来产生中断前. 调 用 free_irq 的位置是设备最后一次被关闭时, 在硬件被告知不要再中断处理器之后. 这 个技术的缺点是你需要保持一个每设备的打开计数, 以便于你知道什么时候中断可以被禁 止.
尽管这个讨论, short 还在加载时请求它的中断线. 这样做是为了你可以运行测试程序而 不必运行一个额外的进程来保持设备打开. short, 因此, 从它的初始化函数
( short_init )请求中断, 不是在 short_open 中做, 象一个真实设备驱动.
下面代码请求的中断是 short_irq. 变量的真正赋值(即, 决定使用哪个 IRQ )在后面显 示, 因为它和现在的讨论无关. short_base 是使用的并口 I/O 基地址; 接口的寄存器 2 被写入来使能中断报告.
if (short_irq >= 0)
{
222
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);
}
}
代码显示, 安装的处理是一个快速处理(SA_INTERRUPT), 不支持中断共享(SA_SHIRQ 没 有), 并且不对系统加密有贡献(SA_SAMPLE_RANDOM 也没有). outb 调用接着为并口使能 中断报告.
由于某些合理原因, i386 和 x86_64 体系定义了一个函数来询问一个中断线的能力:
int can_request_irq(unsigned int irq, unsigned long flags);
这个函数当试图分配一个给定中断成功时返回一个非零值. 但是, 注意, 在 can_request_irq 和 request_irq 的调用之间事情可能一直改变.