中断与中断处理

中断

中断就是使硬件发出通知给处理器,比如在敲击键盘的时候,键盘控制器会发送一个中断,通知操作系统有键按下,让操作系统尽快处理
不同的设备对应的中断不同,每个中断都通过一个唯一的数字标志,使得操作系统能够对中断进行区分,继而提供不同的处理程序.这些中断值通常被成为中断请求(IRQ)线.每个IRQ先都会被关联一个数值量;例如在经典的PC机上,IRQ 0 是时钟中断.1是键盘中断.但是对于连接在PCi总线上的设备而言,中断是动态分配的.

中断处理程序

在操作系统中,在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序,产生中断的每个设备都有一个特定的中断处理程序.
对于操作系统而言,中断处理程序在尽可能短的时间内完成很重要;但是对于硬件而言,操作系统能迅速对其中断进行服务也非常重要.
所以中断程序首先要通知硬件设备中断已接受.还要运行中断函数处理.例如要把硬件网络接收到的数据拷贝到内存,但是现在网速特别块,所以之后的工作量会很大.因此将响应中断分为两部分.

1
在操作系统中一般中断分为两部分.中断处理程序是上半部---接收到一个中断,它就立即开始执行,但是只做有严格实现的工作.例如对接收的中断进行应答或复位硬件.能够允许稍后完后才能的工作会推迟到下半部.

linux的中断处理程序是无须重入的,但一个给定的中断处理程序正在执行,相应的中断线上的所有处理器上的都会被屏蔽掉.

注册中断处理程序

每个设备都有相关的驱动程序,如果硬件使用中断,应该注册一个中断处理程序.通过reques_irq()函数注册,并且激活给定的中断线,以处理中断.

1
2
3
4
5
6
7
8
9
int request_irq(unsigned int irq,  表示要分配的中断号
//handler是一个指针,指向处理这个中断的实际中断处理程序
irq_handler_t handler,
//标志位: IRQF_FISABLED 表示不响应其他中断;IRQF_TIMER为系统定时器的中断处理而准备的;IRQF_SHARED:表明在多个中断处理程序之间共享中断线.
unsigned long flags,
// 是与中断相关的设备的ASCII文本表示
const char *name,
// 拥有共享中断线,dev讲提供唯一的标识信息.当删除中断处理程序需要释放时,能够知道删除哪一个.
void *dev)

request_irq()成功会返回0,否则返回非0值.
这里request_irq函数可能会睡眠,因此不能在中断上下文或者其他不允许阻塞的代码中调用该函数.
中断线共享仅仅指的是中断号共享,并不是处理程序共享.

共享的中断处理程序

对于共享中断线的设备,首先request_irq()的flag参数为IRQF_SHARED标志
对于dev参数必须唯一.指向任意设备结构的指针就可以满足这一要求;通常选择设备结构,因为它是唯一的.这里设备结构是你要想响应的设备结构.
中断处理程序必须能够区分它的设备是否真的产生了中断.这需要硬件的支持,也需要程序中有相关的处理逻辑.

释放中断处理程序

使用free_irq(unsigned int irq, void *dev);
如果中断线是共享的,则传入dev参数,删除此dev对应的处理程序,知道最后一个处理程序时才会被禁用此中断线;如果不是共享的,则在删除处理程序的同时禁用此中断线.

中断上下文

上面提到了中断上下文.其实在执行一个中断处理程序时,内核处于中断上下文中.
中断上下文和进程没有瓜葛,因为没有后备进程,所以中断上下文不可以睡眠.因此在中断上下文中不能调用睡眠的函数.
在中断上下文具有较为严格的时间限制,所有的中断处理程序必须尽可能的迅速,简洁.尽量把工作从中断处理程序中分离出来.放在下半部执行.
在每个处理中有一个中断处理栈,大小为1页. 在栈中处理中断程序.

中断控制

禁止指定中断线

1
2
3
4
void disable_irq(unsigned int irq);//会等待当前处理程序完成后才返回
void diable_irq_nosync(unsigned int irq);//强制返回
void enable_irq(unsigned int irq);
void synchronized_irq(unsigned int irq);

前两个函数禁止中断控制器上制定的中断线.synchronized_irq等待一个特定的中断处理程序的退出.
对于调用禁用函数可以嵌套,但是有一点就是调用多少次禁用中断线函数,就要调用多少次启动函数才能够重新激活.