前言:
学习一门处理器最重要的就是掌握该处理器的指令集和异常处理。
异常概念:
处理器在正常执行程序时可能会遇到一些不正常的事件发生,这时处理器就要将当前的程序暂停下来转去处理这个异常的事件,异常处理后再返回到被异常打断的点继续执行
例如:
我现在正在学习,然后我妈叫我吃饭,这时我要停止学习去吃饭,吃完饭后我就重新到我吃饭之前学习的点继续学习。此时,吃饭就是一个异常事件
对于CPU,假设它在按顺序执行用户程序,而到了某一个位置,CPU要去处理异常,就会跳转到异常处理程序中,按顺序执行。
注意:用户程序和异常处理程序都是编程人员自己写的
假设我正在玩游戏,用户程序就是游戏,在玩游戏的途中,我觉得声音小了,按按钮加大声音,这时,相当于去处理异常了,跳转到了异常处理程序中,运行完后返回用户程序执行异常的那个位置。
异常与中断的区别
他们两个都是计算机系统中的事件,最重要的区别是,中断是外围设备向处理器发出的,可以理解为硬件,而异常是程序执行过程中出现的,可以理解为软件。即中断与硬件有关,异常与软件有关。
中断的产生是异步的,也就是说,它可以在任何时候发生,用于通知处理器要处理的事件,而异常的产生是同步的,也就是说,它只会在程序执行到某个特定的指令时发生,用于表示程序出现了错误或异常情况
处理中断需要保存当前程序的上下文,并跳转到中断服务程序中执行相应操作;处理异常则不需要保存当前程序的上下文,而是立即跳转到异常处理程序中处理异常情况。
异常处理机制
对于不同的处理器,他们对异常的处理流程大致相似,但是不同的处理器在具体的实现机制上有问题:比如处理器遇到哪些事件被认为是异常事件,遇到异常事件后处理器有哪些操作,处理器如何跳转到异常处理程序,如何处理异常,处理完异常后有如何返回到被打断的程序继续执行等。这些细节称为异常处理机制
ARM如何处理异常
ARM异常源
导致异常产生的事件称为异常源
上述处理机制中的第一条,处理器遇到哪些事件被认为是异常事件,就是哪些事件可以打断正在运行的程序
例如正在上课,老师叫李明出去,这只会打断李明的运行,而不会打断其他人。
ARM的7类异常源
FIQ | 快速中断请求引脚有效 |
IRQ | 外部中断请求引脚有效 |
Reset | 复位电平有效 |
Software Interrupt | 执行swi指令 |
Data Abort | 数据终止 |
Prefetch Abort | 执行预取中止 |
Undefined Instruction | 遇到不能处理的指令 |
异常模式
在ARM的8个基本工作模式中有5个属于异常模式,即ARM遇到异常后会切换成对应的异常模式
异常源 | FIQ | IRQ | Reset/SWI | Data Abort/Prefetch Abort | Undefined Instruction |
异常模式 | FIQ | IRQ | SVC | About | Undef |
ARM产生异常后的动作(自动完成)
- 拷贝CPSR中的内容到对应异常模式下的SPSR_<mode>(保存内容)
- 修改CPSR的值
2.1 修改中断禁止位禁止相应的中断
2.2 修改模式位进入相应的异常模式
2.3 修改状态位进入ARM状态 - 保存返回地址对应d下的LR_<mode>(保存地址)
- 设置PC为相应的异常向量(异常向量表对应的地址)
ARM产生异常,会跳转去实现异常处理程序,那么这些动作怎么实现的?
如上,第一步,会将CPSR的内容拷贝到对应异常模式下的SPSR_<mode>
CPSR(Current Program Status Register):当前处理器状态
正如我们所知,ARM处理器做完异常处理后会以当前内容继续执行程序,那么就需要有一个寄存器保存CPSR(当前处理器)的值,即使用SPSR_<mode>来保存CPSR内容。
第二步,修改CPSR值。
我们已经拷贝了之前的CPSR的值,所以可以放心修改。首先要确保它在处理异常时不能被中断。
例如:
我正在写作业,我妈叫我去吃饭,那么我在吃饭的过程中,就不能有其他的中断来影响我,所以直接修改相应的禁止位。
当IRQ禁止位被设置为1时,处理器将禁止所有外部中断的触发。就比如正在处理异常时,任何硬件操作信息都不能中断异常进行,但是异常优先级高的可以停止异常优先级低的先执行高优先级异常。
修改值的第二三步就好理解了,当执行异常处理程序时,模式位会变为相应异常模式,而异常处理程序的状态位是ARM状态。
第三步,保存返回地址。
正如我们所知,异常处理结束后会返回到遇到异常地址的下一个地址,不能执行完异常后自己的家都忘了在哪,所以我们需要保存返回地址,以便回到正确的家。
第四步,设置PC为相应的异常向量。
大家发现没有,看了上面三个步骤,好像都是在原地进行的,也就是在遇到异常的那个地址进行的,修改了很多,也保存了很多,但是并没有跳转到异常处理程序中。那么第四步即是跳转操作了,通过设置PC为相应的异常向量,跳转到异常处理程序中。
异常向量表是什么?
- 异常向量表本质是内存中的一段代码
- 表中为异常源分配了四个字节的存储空间
- 遇到异常后处理器自动将PC修改为对应的地址,如下图,IRQ异常PC将会修改为0x18
思考:
遇到异常后PC会跳转到异常向量表,那么我们能不能在每一个异常向量表下写我们的异常程序?这样可以跳转到对应地址后直接执行,是不是很方便?
答案:
错误,如上述第二点所知,异常向量表只为每个异常源分配了四个字节的内存,而异常程序通常很大,四个字节肯定装不下,所以这种方法是错误的。
那么在对应的异常向量表中,会写一个跳转程序使其跳转到你自己写的异常处理程序入口
ARM异常执行结束后,返回的动作(自己编写)
- 将SPSR_<mode>的值复制给CPSR,使处理器恢复之前的状态(内容)
- 将LR_<mode>的值复制给PC,使程序跳转回被打断的地址继续执行(地址)
至此,ARM遇到异常并执行的过程就是这样
总结一下:
网卡或者其他程序发出异常(假设为IRQ),在遇到异常而没跳转之前,那么会执行开头四步骤,赋值,修改,存地址,PC跳转,其中修改操作要进行三步,中断、模式、状态,PC要跳转到对应的异常向量表的异常源地址。这样实现了跳转操作。跳转后,每一个异常源里有跳转程序,这时才跳转到自己写的异常处理程序入口,也就是说跳转到执行异常程序经历了两次跳转。执行过程就是自己写的程序那样执行,执行结束后,会将之前赋值给SPSR_<mode>(存放的内容)值赋值给CPSR,而LR_<mode>(存放的地址)赋值给PC,使其跳转到被打断的地址而且保持之前状态继续执行。
异常优先级
当两个或者多个异常同时被遇到,那么异常处理的顺序就是重要问题,异常优先级就是处理这个问题的关键。
如下表,多个异常同时产生的服务顺序:
注意一下三个:
- Reset(复位操作)优先级最高
- FIQ比IRQ优先级高
FIQ的响应速度比IRQ快的原因:
- FIQ在异常向量表中位于表尾,所以可以直接把异常处理程序写到异常向量表之后,省去跳转时间。
- FIQ模式有5个私有寄存器(r8~r12),执行中断处理程序前无需压栈保存寄存器,可直接处理中断
- FIQ的优先级高于IRQ
- 两个中断同时发生先响应FIQ
- FIQ可以打断RIQ,但是RIQ不能打断FIQ