Linux中断子系统
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux中断子系统相关的知识,希望对你有一定的参考价值。
1. 基础概念
1.1 中断类型
同步中断由CPU本身产生,又称为内部中断。这里同步是指中断请求信号与代码指令之间的同步执行,在一条指令执行完毕后,CPU才能进行中断,不能在执行期间。所以也称为异常(exception)。
异步中断是由外部硬件设备产生,又称为外部中断,与同步中断相反,异步中断可在任何时间产生,包括指令执行期间,所以也被称为中断(interrupt)。外部硬件设备需要连接到CPU的外部中断引脚上。
1.2 中断或异常处理
中断处理过程:设备产生中断,并通过中断线将中断信号送往中断控制器,如果中断没有被屏蔽则会到达CPU的INTR引脚,CPU立即停止当前工作,根据获得中断向量号从IDT中找出门描述符,并执行相关中断程序。
异常处理过程:异常是由CPU内部发生所以不会通过中断控制器,CPU直接根据中断向量号从IDT中找出门描述符,并执行相关中断程序。
1.3 中断VS异常
外中断就是我们常说的的中断,是指由于外部设备事件所引起的中断,如通常的磁盘中断、打印机中断等 <VS> 内中断就是异常—,是指由于 CPU 内部事件所引起的中断,如程序出错(非法指令、地址越界)。内中断(trap)也被译为“捕获”或“陷入”。
异常是由于执行了现行指令所引起的,由于系统调用引起的中断属于异常。 <VS> 中断则是由于系统中某事件引起的,该事件与现行指令无关
总结:驱动代码中使用的都是中断,而不是异常。也就是说,驱动代码中的中断都是可以通过查询SOC手册,找到对应外设的控制器使用的哪些中断。
1.4 缩写:
NMI Non-maskable interrupt 不能屏蔽的中断
GIC Global Interrupt Controller 全局中断控制器
GIC Generic Interrupt Controller 通用中断控制器
2. Linux中断子系统
2.1 概述
在Linux kernel中,使用两个 ID 来标识一个来自外设的中断:
一是 IRQ number。CPU需要为每一个外设备中断编号,称之为 IRQ Number,这个IRQ Number是一个虚拟的interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断。
二是HW interrupt ID。对于interrupt controller而言,它收集了多个外设的interrupt request line并向上传递,interrupt controller用HW interrupt ID来标识外设的中断。
对于驱动工程师而言,我们和 CPU 的视角是一样的,我们只希望得到一个 IRQ Number,而不关心具体是哪个 interrupt controller 上的哪个 HW interrupt ID。
当 interrupt controller 级联的情况下,仅仅用 HW interrupt ID 已经不能唯一标识一个外设中断,还需要知道该 HW interrupt ID 所属的 interrupt controller (HW interrupt ID在不同的interrupt controller上是会重复编码的)。
Linux kernel 中的中断子系统需要提供一个将 HW interrupt ID 映射到 IRQ Number 上来的机制。
2.2 中断源和中断控制器
系统中所有的interrupt controller会形成树状结构,对于每个interrupt controller都可以连接若干个外设的中断请求(我们称之为interrupt source),interrupt controller会对连接其上的interrupt source(根据其在interrupt controller中物理特性)进行编号(也就是HW interrupt ID),但这个编号仅仅限制在本interrupt controller范围内。
2.3 中断描述符
在Linux kernel中,对于每一个外设的IRQ都用struct irq_desc来描述,我们称之为中断描述符,kernel中会有一个数据结构(中断描述符表IDT)保存了所有IRQ的中断描述符。
系统中每一个连接外设的中断线(irq request line)都用一个中断描述符来描述,每一个外设的中断线都分配一个中断号(IRQ Number),系统中有多少中断线(中断源)就有多少个描述符。
2.4 中断的处理过程
当发生中断以后,首先获取触发中断的HW interrupt ID,然后通过HW interrupt ID找到IRQ Number,然后通过IRQ Number就可以获取对应的中断描述符,最后调用中断描述符中的handler来进行中断处理。
2.5 接口(request_irq/free_irq:)
int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
// irq: 要注册中断服务函数的中断号,定义在mach/irqs.h
// handler: 要注册的中断服务函数
// irqflags: 触发中断的方式,比如共享中断、边沿触发,定义在linux/interrupt.h
// devname: 中断程序的名字
// dev_id: 传入中断处理程序的参数,注册为共享中断时不能为NULL,因为卸载时需要这个做参数,避免卸载其它中断服务函数
void free_irq(unsigned int irq, void *dev_id)
// irq: 要卸载的中断号
// dev_id: 要卸载的中断action下的哪个服务函数
随着计算机设备的增加,一个中断线号对应一个中断处理程序已经不太现实,这个时候就出现了共享的中断线号,多个设备使用同一个中断线号,同一个中断线号的所有中断处理程序连接成一个链表,当共享中断线上产生一个中断的时候,就要遍历其对应的处理程序链表,调用所有的处理程序,处理程序内部根据第四个参数dev_id进行判断是不是这个中断处理程序对应的设备产生的中断,如果不是立即返回,如果是则处理完成,如果链表中没有一个是,则说明出现错误。
以上是关于Linux中断子系统的主要内容,如果未能解决你的问题,请参考以下文章
linux kernel的中断子系统之:IRQ number和中断描述符
Android 逆向Linux 文件权限 ( Linux 权限简介 | 系统权限 | 用户权限 | 匿名用户权限 | 读 | 写 | 执行 | 更改组 | 更改用户 | 粘滞 )(代码片段