操作系统 原理与实现 LEC3:中断,异常与系统调用
前言
引入:OS如何与外设交互?
外设具有控制器和缓冲区,将输出存入缓冲区
OS获取输入的两种可能方法
- 轮询:不断读取缓冲区的值
- 中断:控制器接收到输入后,打断CPU正常执行,OS进行处理
实例:
显示器:60Hz,对CPU而言频率很低,可以采取中断响应
网络:中断+一段时间的轮询(保证快速响应)
应用一般不直接操作设备,而是从OS获取输入(部分用户态驱动除外),例如getchar()调用read()请求OS将输入返回给程序。
通用概念
-
中断
- 外部硬件设备产生的信号。
- 中断是异步的,产生的原因和当前执行的指令无关。
-
异常
- 软件程序执行产生的事件,包括系统调用。
- 异常是同步的,产生和执行与当前执行或试图执行的指令有关。
不同体系结构术语的对应关系
之后提到的中断、异常采取上面的通用概念。
AArch64的中断(异步异常)
- 重置(Reset)
- 最高级别的异常
- 由系统上电,控制软件,Watchdog等触发
- 中断(Interrupt)
- CPU外部信号触发,打断当前执行
- 如计时器中断、键盘中断等
AArch64的异常(同步异常)
- 中止(Abort)
失败的指令获取或数据访问 - 异常产生执行
- SVC(Supervisor Call)
当用户空间通过系统调用陷入到内核空间的时候,则最终会通过SVC指令进入到内核空间 - HVC(Hypervisor Call)
当在ARMv8-A架构下,normal world, EL1尝试去访问EL2的时候,则会陷入到虚拟化层的,其中是通过HVC指令 - SMC(Secure Moniter Call)
用于切换noramal world 和 secure world使用。
- SVC(Supervisor Call)
中断的产生
AArch64中断(异步异常)的产生
早期:向量中断控制器。
ARM处理器中的GIC(Genetal Interrupt Controller)可以管理中断。
需要考虑的问题:
- 如何定义优先级
- 交给谁处理
- 如何与软件协同
AArch64中断(异步异常)的分类
- IRQ
普通中断,优先级低,处理慢。 - FIQ
快速中断,优先级高,处理快。
一次只能有一个FIQ,常为可信任的中断源预留。
IRQ和FIQ连接CPU的不同针脚,由GIC进行控制。
- SError
难以定位和处理的异常,多由异步中止导致。
GIC中断来源
如图所示
-
SPI 共享外围中断
- 由所有CPU共同连接的设备触发
- 可以被路由到一个或多个核,找到可用的核进行处理
- Distributor可配置路由
- 如UART中断
- 中断ID:32-1019 4096-5119
-
PPI 私有设备中断
- 由每个处理器核上的私有设备触发
- 指定核处理
- 如通用定时器(Generic Timer)
- 中断ID:16-31 1056-5119
-
SGI 软件产生中断
- 发送核间中断进行通信
- 中断ID:0-15
GIC的路由配置
以启用Timer为例:使用MMIO,设置GIC中寄存器,启用timer
GIC中断信息获取
使用MMIO,从GIC中的寄存
器里获得中断信息
Linux中的中断处理
- 尽快返回 + 延迟处理
- Top Half + Bottom Half
中断处理中的一些约束
- 不能睡眠或者调用可能会睡眠的任务
- 不能调用schedule()调度
- 不能释放信号或调用可能睡眠的操作
- 不能和用户地址空间交换数据
中断和异常的处理
处理流程
中断和异常处理的必做事项
- 进入中断或异常时
- 需保存处理器状态,方便之后恢复执行
- 需准备好在高特权级下进行执行的环境
- 需选择合适的异常处理器代码进行执行
- 需保证用户态和内核态之间的隔离
- 处理时
- 需获得关于异常的信息,如系统调用参数、错误原因等
- 返回时
- 需恢复处理器状态,返回低特权级,继续正常执行流
AArch64的异常处理流程
异常发生-信息保存,切换到EL1
异常或中断发生后,硬件会将错误码和部分上下文信息存储在寄存器中
– 处理器状态(PSTATE)-> Saved Program Status Register
(SPSR_EL1)
– 当前指令地址(PC)-> Exception Link Register(ELR_EL1)
– 异常发生原因 ->
- Serror与异常:Exception Syndrome Register(ESR_EL1)
- 中断:GIC中的寄存器(使用MMIO读取)
• 安全性问题
– 上述寄存器均不可在用户态(EL0)中访问
• 硬件会适当修改处理器状态(PSTATE),进入EL1执行
• 问题:栈内存的安全性
– 进入EL1级别后,栈指针(SP)会自动换用SP_EL1
– 从而实现用户栈->内核栈
– 如需在EL1下使用SP_EL0作为栈指针,可配置SPSel寄存器
异常处理-寻址handler
• 使用异常向量表(Exception Vector Table)
– 每个异常级别存在独立的异常向量表
– 表项为异常向量(Exception Vector),是处理异常或跳转
到异常handler的小段汇编代码
– 地址位于VBAR_EL1寄存器中
– 选择表项取决于
• 异常类型(同步、IRQ、FIQ、Serror)
• 异常发生的特权级
• 异常发生时的处理器状态(使用的栈指针/运行状态)
返回
eret 指令
– ELR_EL1 -> PC,恢复PC状态
– SPSR_EL1 -> PSTATE,恢复处理器状态
– 降至EL0,硬件自动使用SP_EL0作为栈指针
– 恢复执行
chcore的异常处理
系统调用
- 指运行在用户空间的程序向操作系统内核请求需要更
高权限运行的服务 - 系统调用提供用户程序与操作系统之间的接口