谈谈对中断的理解
一.中断的理解
中断是指CPU在执行过程中,出现了突发事件,CPU必须暂停当前程序的运行,保持现场,转而处理突发事件,处理完毕之后,恢复现场继续执行。
中断按照来源可以分为:
- 内部中断:来自CPU内部,通常由软件中断指令和一些异常错误触发
- 外部中断:来自CPU外部,通常由外设触发,经由中断控制器转发请求到CPU,然后进行处理
二.单片机的中断
在玩单片机的时候,中断用的最多的就是外设触发中断了基本就是:
- 中断源的优先级与打开使能中断通道
- 绑定EXIT与GPIO,配置中断触发方式,引脚是浮空输入
- 编写中断服务程序
这个阶段对中断的理解还停留在:外设触发中断、CPU响应中断、执行中断服务程序。
三.ARM的异常中断
在ARM的学习过程中,对异常中断又有了更深入的理解,涉及到ARM的工作模式、异常中断、中断向量表、环境保存与恢复等
ARM工作模式&&异常中断&&中断向量表
从ARM的工作模式到异常中断:
以及ARM的中断向量表,一般是32Bytes,一个异常中断占4Bytes,一般是一个跳转指令:
其中,中断优先级的划分如下:
可以看出:
- 复位就是直接去0x0000_0000地址执行
- FIQ放在中断向量表最后面,可以直接在中断向量表后编写FIQ处理程序,呼应了快速中断
- FIQ的优先级比IRQ高,可以打断IRQ
- 系统调用就是利用SWI(软件中断)进行处理的
进入中断和退出中断的操作
- 将下一条指令地址保存在lr寄存器中,具体是pc+4还是pc+8取决于异常的种类
- 将当前的CPSR拷贝到对应的SPSR
- 根据异常修改CPSR的值
- 根据向量表跳转到中断处理程序执行,在中断处理程序中进行环境保存与恢复
退出异常中断时的情况:
- 将lr寄存器减去相应的偏移,赋值给pc
- 将SPSR中的值赋值给CPSR
- 清除中断标志位(如果在进入中断是设置了)
中断异常服务程序
中断中断服务程序一般要做的是:
/* und异常处理,进入异常前,硬件完成的事情:将CPSR拷贝到SPSR,将被中断指令的地址存储在lr中 */
do_und:
ldr sp, =0x34000000 /* und的栈指针,指向64M 的SDRAM的最高地址,为C函数分配空间 */
stmdb sp!, {r0-r12,lr} /* 保存现场 */
mrs r0, cpsr /* mrs读出寄存器的值,通过r0寄存器向下面的函数传参 */
bl Und_Process
ldmia sp!, {r0-r12, pc}^ /* 恢复现场,注意:一定要加!来保存sp的改变 */
- 设置栈,通过sp_und来设置
- 保存现场,包括r0~r12寄存器、lr寄存器,保存lr也是必须的,因为lr中的是异常处理完之后的返回地址
- 调用C处理函数
- 恢复现场,利用ldmia sp!, {r0-r12,pc}^ 恢复各个寄存器的值,将lr寄存器的值赋值给pc寄存器(有待考证,ia是先 后 ),^顺便把SPSR中的值恢复到CPSR中
所谓保持现场与恢复现场就是对r0~r12寄存器进行入栈和出栈
中断控制器
在初始化中断控制器的时候,主要是打开使能位即可,SRCPND和INTPND是在中断处理函数里使用的,判断是哪一个中断请求的
四.Linux的中断
Linux中接触操作系统之后,有了中断、异常、系统调用的概念,
中断、异常、系统调用
硬件上的处理:在CPU初始化的时候设置中断使能标志,这个中断使能标志对异常、中断、系统调用都是等效的。然后根据内部或者外部事件设置中断标志位,根据中断向量表调用相应的中断服务例程。
所以,无论是中断、异常、系统调用,都会在中断向量表中进行跳转处理,接下来就是针对性的服务了。
如果是中断,直接进入设备驱动中,反馈(鼠标、键盘的输入);
如果是异常,直接转到异常服务例程来做处理;
如果是系统调用,由于系统调用的量很大,不同的系统调用在系统调用表中区分,选择不同的系统调用实现;
当然,在处理异常中断的时候,要注意保护现场和恢复现场,可以参考ARM来理解。
Kernel的中断处理机制
由于中断会打断进程的正常调度与运行,势必要求中断服务程序尽量短小精悍,然而大多数中断处理程序中的工作量不会很小。
Linux内核的中断处理框架是将中断的处理分为了上半部和下半部:
当然,可以在/proc/interrupts中查看系统中断的统计信息,包括每个中断号上的中断在CPU上发生的次数。
Linux设备驱动中使用中断的设备需要申请和释放中断,使用内核提供的request_irq()和free_irq()接口。
中断下半部的实现
Linux实现中断下半部的机制主要有tasklet、工作队列、软中断、线程化irq。
tasklet的执行上下文是软中断,执行的时机是上半部返回的时候,只需要定义tasklet及其处理函数,并关联两者就可以。关于tasklet的调度,在需要调度tasklet的时候引用一个tasklet_schedule()函数就可以。
工作队列的执行上下文是内核线程,所以是可以调度和睡眠的。
软中断是一种传统的下半部处理机制,执行时机通常是上半部返回的时候。
五.中断作为计算机系统中必要的存在
从UBoot、STM32等的启动代码中也可以看出,中断向量表为计算机系统的一种基础服务存在。