Linux 设备驱动系列 (13) —— 系统中断编程
![Linux设备驱动系列(13) —— 系统中断编程](https://static001.geekbang.org/infoq/46/4691e67c81ffea2fed3ddf2b487c95e0.png)
关注微信公众号:Linux 内核拾遗
前文已经介绍了系统中断的基础知识,本文立刻马上进入系统中断的编程实战。
1 中断编程特点
和普通的内核编程不同,中断代码的编写有如下的特点(或者需要注意的地方):
避免睡眠:中断处理程序不能调用会导致睡眠的函数。
使用自旋锁:进入临界区时,用自旋锁代替互斥锁,因为互斥锁会导致睡眠。
禁止与用户空间交换数据:中断处理程序不应直接与用户空间交互。
快速处理并延迟工作:中断处理程序应快速执行,复杂任务应放在下半部分,通过软中断(softirq)、任务队列(tasklet)或工作队列(workqueue)处理。
防止重入:确保处理中断时禁用相应的 IRQ,防止重复调用。
合理设置优先级:中断处理程序可以被更高优先级的中断打断,必要时标记为快速处理,但过多会影响性能。
2 中断相关函数
下面介绍一下中断相关的基本内核函数。
2.1 request_irq
函数 request_irq()用于向内核中注册一个中断处理函数:
当内核接收到中断号为 irq 的中断时就会调用 handler 函数进行处理。
flags 的相关定义在头文件linux/interrupt.h
中:
IRQF_DISABLED:指示内核在执行中断处理程序时禁用所有中断,大部分情况下不应该设置此 flag,必须时候保留给执行速度快且性能敏感的中断。
IRQF_SAMPLE_RANDOM:指定由此设备生成的中断应贡献到内核熵池,其中内核熵池提供由各种随机事件派生的真正随机数。不要在设备以可预测的速率发出中断(例如系统计时器)或可能被外部攻击者影响(例如网络设备)时设置此标志。
IRQF_SHARED:指定中断线可以在多个中断处理程序之间共享,否则每条中断线上只能存在一个处理程序。
IRQF_TIMER:指定此处理程序处理系统计时器的中断。
name 指定的是使用该 IRQ 的设备名称,它会显示在/proc/interrupts 中。
dev_id:如果中断线未共享,可以传递 NULL
;如果中断线是共享的,必须传递一个唯一标识符,用于在共享中断线上唯一标识中断处理程序,确保内核能够正确移除特定的处理程序。
需要注意的是,request_irq()不能在中断上下文(或者其他不应该阻塞的代码)中调用,因为该函数调用会导致阻塞。
2.2 free_irq
函数 free_irq()用于释放由 request_irq()注册的中断处理函数:
如果指定的中断线未共享,此函数会移除处理程序并禁用该中断线。如果中断线是共享的,通过 dev_id
标识的处理程序会被移除,但只有在最后一个处理程序被移除时才会禁用中断线。
在共享中断线的情况下,需要一个唯一的标识符来区分同一条线上可能存在的多个处理程序,并使 free_irq()
能够只移除正确的处理程序。无论中断线是否共享,如果 dev_id
非空,它必须与所需的处理程序匹配。
调用 free_irq()
必须在进程上下文中进行。
2.3 其他函数
enable_irq:启用指定的中断线,允许相应的中断请求(IRQ)触发中断处理程序。
disable_irq:禁用指定的中断线,阻止该中断线触发中断处理程序。此函数等待所有正在处理的中断处理程序完成后再返回。
disable_irq_nosync:禁用指定的中断线,但不等待当前正在处理的中断处理程序完成。这可以提高性能,但需要确保不会导致竞态条件。
in_irq:用于确定代码是否在硬中断处理程序(Interrupt handler)中运行。
in_interrupt:检查当前是否在硬中断(Interrupt handler)或者软中断处理程序(Bottom half)中运行。
2.4 使用示例
3 中断模拟
下面介绍一下如何通过软件方式来模拟触发硬件中断。
Intel 处理器使用中断描述符表 (IDT) 来处理中断。IDT 由 256 个条目组成,每个条目对应一个向量,并且每个条目占 8 个字节。所有条目都是指向中断处理函数的指针。IDT 的地址存储在 IDTR(IDT 寄存器)中。它们之间的关系可以如下图所示:
![](https://static001.geekbang.org/infoq/ce/cebe7705953976a7841bc72f2005d455.png)
代码中可以通过指令"int"来触发一个中断,例如"int $0x80"将触发一个 Linux 系统调用。
Linux 内核的中断向量表映射关系定义在arch/x86/include/asm/irq_vectors.h
头文件中:
![](https://static001.geekbang.org/infoq/69/69c93a0b520ec7850a166e4ef96d5b27.png)
其中外部中断源可用的 IDT 向量是从 0x20 开始的,并且 0x80 分配给系统调用向量,0x30~0x3f 预留给 ISA。
因此执行asm("int $0x3B")
将会往 IRQ 11 发起中断。
4 完整示例代码
kernel_driver.c
关注微信公众号:Linux 内核拾遗
版权声明: 本文为 InfoQ 作者【Linux内核拾遗】的原创文章。
原文链接:【http://xie.infoq.cn/article/223a881edc271c32a29a19828】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论