本文最后更新于331 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com
CPU中异常与中断
CPU 在运行的过程中,也会被各种“异常”打断。这些“异常”有:
- SWI(软中断)
- 快中断
- 中断(中断也属于一种“异常”,导致中断发生的情况有很多,比如:)
- 按键
- 定时器
- ADC 转换完成
- UART 发送完数据、收到数据
这些众多的“中断源”,汇集到“中断控制器”,由“中断控制器”选择优先级最高的中断并通知 CPU。
中断的处理流程
arm 对异常(中断)处理过程:
- 初始化:
- 设置中断源,让它可以产生中断
- 设置中断控制器(可以屏蔽某个中断,优先级)
- 设置 CPU 总开关(使能中断)
- 执行其他程序:正常程序
- 产生中断:比如按下按键—>中断控制器—>CPU
- CPU 每执行完一条指令都会检查有无中断/异常产生
- CPU 发现有中断/异常产生,开始处理。
- 这些函数做什么事情?
- 软件做的:
- 保存现场(各种寄存器)
- 处理异常(中断):分辨中断源,再调用不同的处理函数
- 恢复现场
异常向量表
/* _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 /* 中断请求(IRQ)处理程序的跳转地址 */
ldr pc, _fiq /* 快速中断请求(FIQ)处理程序的跳转地址 */
Linux 系统对中断的处理
进程、线程、中断的核心:栈
栈
中断中断,中断谁?
中断当前正在运行的进程、线程。
进程、线程是什么?内核如何切换进程、线程、中断?
要理解这些概念,必须理解栈的作用。
中断当前正在运行的进程、线程。
进程、线程是什么?内核如何切换进程、线程、中断?
要理解这些概念,必须理解栈的作用。
ARM 处理器程序运行的过程
ARM 芯片属于精简指令集计算机(RISC:Reduced Instruction SetComputing),它所用的指令比较简单,有如下特点:
1.对内存只有读、写指令
2.对于数据的运算是在 CPU 内部实现
3.使用 RISC 指令的 CPU 复杂度小一点,易于设计
4.比如对于 a=a+b 这样的算式,需要经过下面 4 个步骤才可以实现:
2.对于数据的运算是在 CPU 内部实现
3.使用 RISC 指令的 CPU 复杂度小一点,易于设计
4.比如对于 a=a+b 这样的算式,需要经过下面 4 个步骤才可以实现:
细看这几个步骤,有些疑问:
1.读 a,那么 a 的值读出来后保存在 CPU 里面哪里?
2.读 b,那么 b 的值读出来后保存在 CPU 里面哪里?
3.a+b 的结果又保存在哪里?
1.读 a,那么 a 的值读出来后保存在 CPU 里面哪里?
2.读 b,那么 b 的值读出来后保存在 CPU 里面哪里?
3.a+b 的结果又保存在哪里?
cpu
CPU 运行时,先去取得指令,再执行指令:
1.把内存 a 的值读入 CPU 寄存器 R0
2.把内存 b 的值读入 CPU 寄存器 R1
3.把 R0、R1 累加,存入 R0
4.把 R0 的值写入内存 a
1.把内存 a 的值读入 CPU 寄存器 R0
2.把内存 b 的值读入 CPU 寄存器 R1
3.把 R0、R1 累加,存入 R0
4.把 R0 的值写入内存 a
程序被中断时,怎么保存现场
保存现场
从上图可知,CPU 内部的寄存器很重要,如果要暂停一个程序,中断一个程序,就需要把这些寄存器的值保存下来:这就称为保存现场。
保存在哪里?内存,这块内存就称之为栈。
程序要继续执行,就先从栈中恢复那些 CPU 内部寄存器的值。
这个场景并不局限于中断,下图可以概括程序 A、B 的切换过程,其他情况是类似的:
保存在哪里?内存,这块内存就称之为栈。
程序要继续执行,就先从栈中恢复那些 CPU 内部寄存器的值。
这个场景并不局限于中断,下图可以概括程序 A、B 的切换过程,其他情况是类似的:
- 函数调用
- 在函数 A 里调用函数 B,实际就是中断函数 A 的执行。
- 那么需要把函数 A 调用 B 之前瞬间的 CPU 寄存器的值,保存到栈里;
- 函数 B 返回之后,就从栈中恢复函数 A 对应的 CPU 寄存器值,继续执行
- 中断处理
- 进程 A 正在执行,这时候发生了中断。
- CPU 强制跳到中断异常向量地址去执行,
- 这时就需要保存进程 A 被中断瞬间的 CPU 寄存器值,
- 可以保存在进程 A 的内核态栈,也可以保存在进程 A 的内核结构体中。
- 中断处理完毕,要继续运行进程 A 之前,恢复这些值。
- 进程切换
- 在所谓的多任务操作系统中,我们以为多个程序是同时运行的。
- 如果我们能感知微秒、纳秒级的事件,可以发现操作系统时让这些程序依次执行一小段时间,进程 A 的时间用完了,就切换到进程 B。
- 怎么切换?
- 切换过程是发生在内核态里的,跟中断的处理类似。
- 进程 A 的被切换瞬间的 CPU 寄存器值保存在某个地方;
- 恢复进程 B 之前保存的 CPU 寄存器值,这样就可以运行进程 B 了。
所以,在中断处理的过程中,伴存着进程的保存现场、恢复现场。进程的调度也是使用栈来保存、恢复现场:
进程、线程的概念
进程、线程
在 Linux 中:资源分配的单位是进程,调度的单位是线程。也就是说,在一个进程里,可能有多个线程,这些线程共用打开的文件句柄、全局变量等等。
而这些线程,之间是互相独立的,“同时运行”,也就是说:每一个线程,都有自己的栈。如下图示:
而这些线程,之间是互相独立的,“同时运行”,也就是说:每一个线程,都有自己的栈。如下图示:
中断处理原则
1:不能嵌套
在 Linux 系上中断无法嵌套:即当前中断 A 没处理完之前,不会响应另一个中断 B(即使它的优先级更高)。
2:越快越好
中断的处理过程中,该 CPU 是不能进行进程调度的,所以中断的处理要越快越好,尽早让其他中断能被处理──进程调度靠定时器中断来实现。
评论