<!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } -->
Kernel Korner - Dynamic Interrupt Request Allocation for Device Drivers
From Issue #132
April 2005
Mar 01, 2005 By Dr B. Thangaraju
inSoftware
中断是处理器和设备沟通的大门。在设备驱动开发中,申请中断请求线(后文简称中断)以及如何处理中断是至关重要的。中断的数量是有限的,共享中断可以让处理器访问更多的设备。尝试申请已经使用的中断会破坏系统。这篇介绍了中断的使用以及如何在一个字符设备中使用中断。
中断是一个硬件机制,可以使设备和处理器交互。
在2.6内核中,Linux中,如果一个进程在kernel mode,如果任意一个高优先级的进程到了ready-to-run的进程队列,低优先级的进程就不能在该高优先级的进程返回用户模式之前抢占。但是,一个中断可以使CPU转移注意力,即便进程在内核模式内执行。当一个中断发生时,CPU挂起当前的任务,执行一些中断相关的代码。
每一个在计算机中的设备有一个设备控制器,当他需要CPU服务时,就会通过一个硬件引脚发出信号。这个硬件引脚和CPU的中断引脚相连。处理器中连接到控制器处理中断的引脚叫中断请求线(IRQ, Interrupt request line)。CPU有若干个这样的引脚。在现代操作系统中,有意个可编程的中断控制器(PIC,programmable interrupt controller)来管理处理器和各种设备控制器之间的IRQ。IRQ是有限的,Linux通过共享中断线来增加可以产生中断的设备。
当CPU执行一个进程,一个设备发送中断到CPU,比如准备传输数据。当一个中断到来,CPU立即保存当前kernel mode stack的 program counter,然后执行对应的中断服务(ISR,interrupt service routine)。一个ISR是一个处于kernel中的函数,用来处理中断的一些请求,比如移动数据等等。执行完ISR,CPU就恢复到之前的进行,然后接着执行。
设备驱动程序是一个在kernel中的软件模块,等到来自应用程序的请求。当一个程序需要从设备中读取数据,对应的设备驱动器就会立即打开,然后对应的设备就会被打开准备读取。如果系统在等待一个慢速设备,就不能做其他事情。为了避免等待,kernel把等待的工作交给设备控制器,然后恢复执行之前的进程。当读完毕之后,设备通过中断通知CPU,CPU执行相应的ISR。
Interrupt classification
中断分为两类,同步和异步。同步中断是CPU control unit在执行指令时产生的。控制单元在终止指令之后产生一个中断,就命名为同步。异步中断是硬件按照CPU时钟在任意时间产生的。在Intel系统中,同步中断被称之为异常,异步中断称之位中断。中断通过一个无符号的one-byte整数表示,称之为向量,范围是0~255。最开始的32个向量(0~31)用于异常和不可屏蔽中断,32~47是可屏蔽中断,由IRQs(0-15)引起。最后的48~255被标记为软件中断。
IRQ Allocation
通过/proc/interrupt可以显示当前注册的中断。比如:
CPU0 CPU1
0: 112559049 0 IO-APIC-edge timer
1: 143195 0 IO-APIC-edge i8042
8: 1 0 IO-APIC-edge rtc0
9: 36 0 IO-APIC-fasteoi acpi
12: 2134767 0 IO-APIC-edge i8042
14: 430047 0 IO-APIC-edge ata_piix
15: 318204 0 IO-APIC-edge ata_piix
16: 64752 0 IO-APIC-fasteoi i915@pci:0000:00:02.0
17: 0 0 IO-APIC-fasteoi eth0
18: 0 0 IO-APIC-fasteoi r852, mmc0
NMI: 0 0 Non-maskable interrupts
LOC: 16015255 66885938 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 0 0 Performance monitoring interrupts
PND: 0 0 Performance pending work
RES: 9789301 11248394 Rescheduling interrupts
CAL: 69042 74498 Function call interrupts
TLB: 96130 208644 TLB shootdowns
TRM: 0 0 Thermal event interrupts
THR: 0 0 Threshold APIC interrupts
MCE: 0 0 Machine check exceptions
MCP: 216 214 Machine check polls
ERR: 7
MIS: 0
第一列是中断号,第二列是系统开机以来产生的中断次数。第三列是PIC相关,第四列是与中断相关的设备名字。
最简单的动态加载一个设备驱动的办法是,首先找到一个没有用的IRQ。request_irq用来申请设备的中断。
Int request_irq (unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *device, void *dev_id);
第一个参数是中断号,我们想从系统中申请的中断号。
Handler是中断处理函数。
Flags是中断标识。SA_INTERRUPT是设置快速中断,SA_SHIRQ是设置共享中断。SA_PROBE是用该中断来probe一个设备。SA_RANDOM是系统随机数生成器的种子。
Device是设备名字。
dev_id是设备id,一个指向struct device的指针。
request_irq如果成功,返回0,如果失败,就返回-EBUSY。
free_irq用来释放中断:
free_irq(unsigned int irq, void * dev_id);
ISR会在中断发生的时候调用。内核维护了一张表,表中是中断处理的地址,也就是中断向量表。当一个中断发生,处理器检查中断向量表中的地址,然后执行。