1. 中断
Linux内核要对连接到计算机上的所有硬件设备进行管理,首先要能和它们互相通信。从所周知,处理器的速度跟外围硬件设备的速度往往不在一个数量级上。所以,需要一种机制,如果轮询(polling)是一种解决办法,可以让内核定期对设备的状态进行查询,然后做出相应的处理,但这让内核做了不少无用功。
更好的办法是由我们来提供一种机制,让硬件在需要的时候再向内核发出信号。这就是中断机制。中断本质上是一种特殊的电信号,由硬件设备生成,并直接送入中断控制器的输入引脚上,再由中断控制器向处理器发送相应的信号,处理器一经检测到此信号,便中断自己当前工作转而处理中断,最后由OS来负责处理新到来的数据。中断是异步的。
不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识。这些中断值通常被称为中断请求(IRQ)线。比如,IRQ0是时钟中断,而IRQ1是键盘中断。并不是所有的中断号都这样严格定义,像PCI总线上的设备,中断就是动态分配的。
1.1. 异常与中断
异常与中断不同,它在产生时必须考虑与处理器时钟同步。实际上,异常也称为同步中断。比如,在处理器执行到由于编程失误而导致的错误指令的时候,或者在执行期间出现特殊情况(缺页),必须靠内核来处理的,处理器就产生一个异常。
中断的的工作方式类似,其差异只在于中断是由硬件而不是软件引起的。
2. 中断处理程序
在响应一个特定中断的时候,内核会执行一个函数,该函数叫中断处理程序(interrupt handler)或中断服务例程(interrupt service routine,ISR)。产生中断的每个设备都有一个相应的中断处理程序。一个设备的中断处理程序是它设备驱动程序的一部分。中断处理程序与其他内核的真正区别在于:中断处理程序是被内核调用来响应中断的,而它们运行于我们称之为中断上下文的特殊上下文中。
2.1. 上半部与下半部的对比
又想程序运行得快,又想程序完成的工作量太多,这两个目的相互矛盾。鉴于两个目的之间存在不可调和的矛盾,所以需要把中断处理程序分成两半或两个部分。中断处理程序是上半部(top half):接收到一个中断,他就立即开始执行,但只做严格时限的工作,例如对接收的中断进行应答或复位硬件,这些工作都是在所有中断被禁止的情况下完成的。能够被允许稍后完成的工作会推迟到下半部(bottom half)去。此后,在合适的时机,下半部被开中断执行。
3. 注册中断处理程序
驱动程序可以通过下面的函数注册并激活一个中断处理程序,以便处理中断:
int request_irq(unsigned int irq,
irqretrun_t (*handler)(int,void *, struct pt_regs *),
unsigned long irqflags,
const char *devname,
void *dev_id);
第一个参数irq表示要分配的中断号。对于大多数其他设备来说,这个值要么是可以通过探测获取,要么可以通过编程动态确定。
第二个参数hanlder是一个指针,指向处理这个中断的实际中断处理程序。hanhler函数的原型接收三个参数。其中第三个参数irqflags可以是0,也可以是多个标志的掩码。如果是SA_INTERRU