第014课 异常与中断

来自百问网嵌入式Linux wiki
Baiwen root讨论 | 贡献2018年1月17日 (三) 18:20的版本

第001节_概念引入与处理流程

取个场景解释中断

假设有个大房间里面有小房间,婴儿正在睡觉,他的妈妈在外面看书。 问:这个母亲怎么才能知道这个小孩醒? 1 过一会打开一次房门,看婴儿是否睡醒,让后接着看书

2 一直等到婴儿发出声音以后再过去查看,期间都在读书

第一种 叫做查询方式: 优点:简单 缺点: 累 写程序如何:

while(1)
{
	1 read book(读书)
	2 open door(开门)
	if()
		return(read book)
	else
		照顾小孩
	
}

第二种叫中断方式: 优点:不累 缺点:复杂

写程序:

 

while(1)
{
	read book
	中断服务程序()//如何被调用?
	{
	处理照顾小孩
	}
}

我们还是看看母亲被小孩哭声打断如何照顾小孩?

母亲的处理过程 1 平时看书 2 发生了各种声音,如何处理这些声音 有远处的猫叫(听而不闻,忽略) 门铃声有快递(开门收快递) 小孩哭声(打开房门,照顾小孩) 3 母亲的处理 只会处理门铃声和小孩哭声 a 现在书中放入书签,合上书(保存现场) b 去处理 (调用对应的中断服务程序) c 继续看书(恢复现场)

不同情况,不同处理 a 对于门铃:开门取快件 b 对于哭声:照顾小孩

我们将母亲的处理过程抽象化 母亲的头脑相当于CPU 耳朵听到声音会发送信号给脑袋,声音来源有很多种,有远处的猫叫,门铃声,小孩哭声。这些声音传入耳朵,再由耳朵传给大脑,除了这些可以中断母亲的看书,还有其他情况,比如身体不舒服,有只蜘蛛掉下来,对于特殊情况无法回避,必须立即处理

对比我们得arm系统

---》图2 ![](https://i.imgur.com/Yg9tRdq.png)

有CPU 有中断控制器,我们的中断源发送 中断控制器可以发信号给CPU告诉它发生了那些紧急情况 中断源有 按键 定时器 有其它的(比如网络数据) 这些信号都可以发送信号给中断控制器,再由中断控制器发送信号给CPU表明有这些中断产生了,这些成为中断(属于一种异常)

还有什么可以中断CPU运行 指令不对,数据访问有问题 reset信号,这些都可以中断CPU 这些成为异常中断

重点在于 保存现场以及恢复现场

处理过程 a 保存现场(各种寄存器) b 处理异常(中断属于一种异常) c 恢复现场


arm对异常(中断)处理过程 1 初始化: a 设置中断源,让它可以产生中断 b 设置中断控制器(可以屏蔽某个中断,优先级) c 设置CPU总开关,(使能中断)

2 执行其他程序:正常程序


3 产生中断:按下按键--->中断控制器--->CPU

4 cpu每执行完一条指令都会检查有无中断/异常产生 5 发现有中断/异常产生,开始处理 对于不同的异常,跳去不同的地址执行程序 这地址上,只是一条跳转指令,跳去执行某个函数 (地址) 指的是异常向量 如下就是异常向量表 对于不同的异常都有一条跳转指令


.globl _start
_start: b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq //发生中断时,CPU跳到这个地址执行该指令 **假设地址为0x18**
	ldr	pc, _fiq
//我们先在0x18这里放 ldr pc ,__irq,于是cpu最终会跳去执行__irq代码
//保护现场,调用处理函数,恢复现场

3-5都是硬件强制做的

6 这些函数做什么事情()软件做的 a 保存现场(各种寄存器) b 处理异常(中断): 分辨中断源 再调用不同的处理函数 c 恢复现场

对比母亲的处理过程来比较arm中断的处理过程

中断处理程序怎么被调用? CPU--->0x18 --跳转到其他函数->

做保护现场
调用函数 

分辨中断源 调用对应函数

恢复现场

cpu到0x18是由硬件决定的,跳去执行更加复杂函数(由软件决定)

第002节_CPU模式(Mode)_状态(State)与寄存器

这节课我们来讲CPU的工作模式(Mode) 状态(State)寄存器 7种Mode:

usr/sys
undefined(und)
Supervisor(svc)
Abort(abt)
IRQ(irq)
FIQ(fiq)

2种State:

ARM state
Thumb state

寄存器:

通用寄存器
备份寄存器(banked register)
当前程序状态寄存器(Current Program Status Register);CPSR
CPSR的备份寄存器:SPSR(Save Program Status Register)

我们仍然以这个母亲为例讲解这个CPU模式 这个母亲无压力看书 -->(正常模式) 要考试,看书--->(兴奋模式) 生病---->(异常模式)

可以参考书籍 《ARM体系结构与编程》作者:杜春雷

对于ARM CPU有7种模式 1 usr :类比 正常模式 2 sys :类比的话兴奋模式 3 5种异常模式:(2440用户手册72页) 3.1 und :未定义模式 3.2 svc :管理模式 3.3 abt :终止模式 a 指令预取终止(读写某条错误的指令导致终止运行) b 数据访问终止 (读写某个地址,这个过程出错) 都会进入终止模式 3.4 IRQ: 中断模式 3.5 FIQ: 快中断模式

我们可以称以下6种为特权模式

und :未定义模式
svc :管理模式
abt :终止模式
IRQ :中断模式
FIQ :快中断模式
sys :系统模式

usr用户模式(不可直接进入其他模式) 可以编程操作CPSR直接进入其他模式 ![](https://i.imgur.com/cgghj9L.png) 这个图是有关各个模式下能访问寄存器的,再讲这个图之前我们先引入 2种state

CPU有两种state 1 ARM state 使用ARM指令集,每个指令4byte 2 Thumb state 使用的是Thumb指令集,每个指令2byte

比如同样是 mov R0, R1 编译后 对于ARM指令集要占据4个字节:机器码 对于Thumb指令集占据2个字节:机器码 引入Thumb减少存储空间

百度搜索ARM指令集和Thumb指令的区别


现在先区分下ARM指令集与Thumb指令集

Thumb 指令可以看作是 ARM 指令压缩形式的子集,是针对代码密度的问题而提出的,它具有 16 位的代码密度但是它不如ARM指令的效率高 .Thumb 不是一个完整的体系结构,不能指望处理只执行Thumb 指令而不支持 ARM 指令集.因此,Thumb 指令只需要支持通用功能,必要时可以借助于完善的 ARM 指令集,比如,所有异常自动进入 ARM 状态.在编写 Thumb 指令时,先要使用伪指令 CODE16 声明,而且在 ARM 指令中要使用 BX指令跳转到 Thumb 指令,以切换处理器状态.编写 ARM 指令时,则可使用伪指令 CODE32声明.


下节课会演示使用Thumb指令集编译,看是否生成的bin文件会变小很多

![](https://i.imgur.com/cgghj9L.png) 在每种模式下都有R0 ~ R15 在这张图注意到有些寄存器画有灰色的三角形,表示访问该模式下访问的专属寄存器 比如

mov R0, R8
mov R0, R8

在System 模式下访问的是R0 ~ R8,在所有模式下访问R0都是同一个寄存器

mov R0,R8_fiq

但是在FIQ模式下,访问R8是访问的FIQ模式专属的R8寄存器,不是同一个物理上的寄存器

在这五种异常模式中每个模式都有自己专属的R13 R14寄存器,R13用作SP(栈) R14用作LR(返回地址) LR是用来保存发生异常时的指令地址

为什么快中断(FIQ)有那么多专属寄存器,这些寄存器称为备份寄存器


回顾一下中断的处理过程 1 保存现场(保存被中断模式的寄存器) 就比如说我们的程序正在系统模式/用户模式下运行,当你发生中断时,需要把R0 ~ R14这些寄存器全部保存下来,让后处理异常,最后恢复这些寄存器 但如果是快中断,那么我就不需要保存 系统/用户模式下的R8 ~ R12这几个寄存器,在FIQ模式下有自己专属的R8 ~ R12寄存器,省略保存寄存器的时间,加快处理速度 但是在Linux中并不会使用FIQ模式 2 处理 3 恢复现场

CRSR当前程序状态寄存器,这是一个特别重要的寄存器 SPSR保存的程序状态寄存器,他们格式如下 ![](https://i.imgur.com/P5Tkmq5.png)

首先 M4 ~ M0 表示当前CPU处于哪一种模式(Mode) 我们可以读取这5位来判断CPU处于哪一种模式,也可以修改这一种模式位,让其修改这种模式 假如你当前处于用户模式下,是没有权限修改这些位的 M4 ~ M0对应什么值,会有说明 ![](https://i.imgur.com/YShQWjP.png)

查看其他位 Bit5 State bits表示CPU工作与Thumb State还是ARM State用的指令集是什么 Bit6 FIQ disable当bit6等于1时,FIQ是不工作的 Bit7 IRQ disable当bit5等于1时,禁止所有的IRQ中断,这个位是IRQ的总开关 Bit8 ~ Bit27是保留位 Bite28 ~ Bit31是状态位, 什么是状态位,比如说执行一条指令 cmp R0, R1 如果R0 等于 R1 那么zero位等于1,这条指令影响 Z 位,如果R0 == R1,则Z = 1

beq跳转到xxx这条指令会判断Bit30是否为1,是1的话则跳转,不是1的话则不会跳转 使用 Z 位,如果 Z 位等于1 则跳转,这些指令是借助状态位实现的


SPSR保存的程序状态寄存器 表示发生异常时这个寄存器会用来保存被中断的模式下他的CPSR 就比如我我的程序在系统模式下运行 CPSR是某个值,当发生中断时会进入irq模式,这个CPSR_irq就保存系统模式下的CPSR 我们来看看发生异常时CPU是如何协同工作的 进入异常的处理流程(硬件) ![](https://i.imgur.com/rI6ZYwF.png) 我们来翻译一下: 发生异常时,我们的CPU会做什么事情 1把下一条指令的地址保存在LR寄存器里(某种异常模式的LR等于被中断的下一条指令的地址) 它有可能是PC + 4有可能是PC + 8,到底是那种取决于不同的情况 2 把CPSR保存在SPSR里面(某一种异常模式下SPSR里面的值等于CPSR) 3 修改CPSR的模式为进入异常模式(修改CPSR的M4 ~ M0进入异常模式) 4 跳到向量表


退出异常怎么做? ![](https://i.imgur.com/pJzcOIV.png) 1 让LR减去某个值,让后赋值给PC(PC = 某个异常LR寄存器减去 offset) 减去什么值呢? 也就是我们怎么返回去继续执行原来的程序,根据下面这个表来取值 ![](https://i.imgur.com/9lPkInF.png) 如果发生的是SWI可以把 R14_svc复制给PC 如果发生的是IRQ可以把R14_irq的值减去4赋值给PC 2 把CPSR的值恢复(CPSR 值等于 某一个一场模式下的SPSR) 3 清中断(如果是中断的话,对于其他异常不用设置)