一、linux的中断处理体系结构
Linux内核将所有的中断统一编号,使用一个irq_desc结构数组来描述这些中断:每个数组项对应一个中断(也可能是一组中断,他们共用相同的中断号),里面记录了中断的名称、中断状态、中断标记(比如中断类型、是否共享中断等),并提供了中断的底层硬件访问函数(清除、屏蔽、使能中断),提供了这个中断的处理函数入口,通过它可以调用用户注册的中断处理函数。
通过irq_desc结果结构数组就可以了解中断处理体系结构,irq_desc结构的数据类型在include/linux/irq.h中定义,如下所示:
Struct irq_desc{
Irq_flow_handler_t handle_irq;/*当前中断的处理函数入口*/
Struct irq_chip *chip;/*低层的硬件访问*/
。。。。。。。。。。。。。。。。。。。
Struct irqaction *action;/*用户提供的中断处理函数链表*/
Unsigned int status;/*IRQ状态*/
Const char *name;/*中断名称*/
}
其中handle_irq是中断处理函数的入口,chip提供了中断低层寄存器等的操作。
Linux的中断处理体系结构如下图所示:
中断函数的处理流程如下所示:
1)在发生中断时,CPU执行异常向量vector_irq的代码
2)在vector_irq里面,最终会调用中断处理的总入口函数asm_do_IRQ;
3)asm_do_IRQ里面,最终会调用irq_desc数组项中的handler_irq。
4)handle_irq会使用chip成员中的函数来设置硬件,比如清除中断、禁止中断、使能中断等
5)handle_irq逐个调用用户在action链表中注册的处理函数
二、中断的实现
对于开发者来说最关心的应该是如何在驱动中实现中断,在linux驱动程序中,为设备实现一个中断一般来说包括两个步骤:
1)向内核注册中断
需要使用函数request_irq向内核中注册中断,其函数原型如下所示:
intrequest_irq(unsigned int irq,void (*handler)(int ,void*,struct pt_regs*),unsigned long flags,const char *devname,void *dev_id)
该函数执行成功则返回0,否则返回一个错误码。
其中的dev_id参数必须唯一,这是为共享中断时使用的。
2)实现中断
实现中断函数需要注意一下情况:中断处理函数运行于中断上下文中,他不能向用户空间发送或者接受数据,不能使用可能引起阻塞或是引起调度的函数,因为引起调度的函数只是相对于进程来说的,而中断函数一旦引起调度,调度函数就再也找不到它了,因为中断程序不是一个进程。
3)释放中断
当设备不再需要使用中断时(通常在驱动卸载时),应当把它们返回给系统,使用:
void free_irq(unsigned intirq,void *dev_id)
由中断的释放函数,可以看出dev_id的作用,如果dev_id不唯一,则不知道释放哪个中断处理函数。