文章目录
此文档历史久远, 系学习过程中的知识整理, 难免存在疏漏, 欢迎批评指正!
IRQ
CPU
会在以下场景中打断当前正在运行的指令, 转而去运行其他指令:
- 外部设备完成了某个操作
- 执行了未知指令
- 访问了无法访问的内存
- 某些特定事件被触发
中断理论上分为三种类型:
- 外部中断,
External Interrupt
又叫硬件中断(Hardware Interrupt
). 由外部硬件设备以异步的方式产生. 它一般通过触发CPU
上对应的中断引脚来通知CPU
对其进行处理, 这就是中断请求:IRQ
.
CPU会根据内部时钟不停的(如 Intel-i9-13900k 每隔10ns会检查一次)对引脚的电平进行检查, 如果发现电平变化了, 就知道IRQ
请求来了, 于是会跳转到中断处理例程:ISR: Interrupt Service Routine
.
系统启动时,BIOS
会对ISR进行首轮初始化! - 内部中断,
Internal Interrupt
又叫异常(Exception
). 一般由CPU内部的一些特殊事件触发:/0
错误- 内存访问错误
- 系统调用
- 软中断,
Software Interrupt
又名自陷(Trap
). 由指令触发,如X86
的int
指令, 或ARM
的swi
指令.
由于是指令触发, 故是同步的(CPU会立即跳到ISR,而不会等待电平发生变化)
中断信号
中断信号分为两类:
- 水平触发(level-sensitive) —- 没错!对应了
epoll
模型中的level-triggered
参数!
变化的电平会一直保持, 直到ISR
处理完毕 - 边缘触发(edge-triggered)
电平仅变化一次.ISR
必须立即处理, 否则会导致中断信号丢失
如何区分多个设备的中断?
上面的模型过于简单, 未能描述清楚CPU如何区分不同设备的中断
计算机界解决复杂问题有一个老套路(请参考网络模型) —- 加一层 –> 中断控制器(PIC
: Programmable Interrupt Controller
)
PIC
PIC首次出现于 1976
年, 8085系列. 目前最新的Intel系列仍然使用
PIC帮助CPU将并发的,带优先级的中断信号, 转换成串行的中断信号, 简化了CPU的设计
CPU使用了 IDTR
(Interrupt Descriptor/Vector Table Register
) 寄存器来保存中断向量表.
该寄存此有64位,高32位是表的起始地址,低16位描述表的大小.
当CPU收到中断时, 首先通过IDTR找到中断向量表, 然后通过该表再借助于 IVN
(中断向量号,其有8个引脚连接着总线) 找到对应的 ISR
(中断服务历程).
系统启动时OS会对ISR进行二次初始化!
IVN
中断向量号的区域划分
- 0-31
CPU保留的向量号, 用于CPU内部的异常处理, 硬编码 - 32-255
外部中断向量号. 可由BIOS或OS自行分配. 提供了每个设备对应的中断描述符.
IDTR的C描述
中断描述符包含了中断服务历程 ISR
的地址, 以及一些其他基本信息.
x86平台该结构大小: 8B
x86-64平台: 16B
struct idt_entry {
uint16_t base_lo; // 中断服务例程的低16位,task gate没有这个字段
uint16_t sel; // 段选择子
uint8_t always0; // 保留字段,必须为0
union {
uint8_t flags; // 中断描述符标志
struct {
uint8_t type : 3; // 中断门类型
uint8_t size : 1; // 中断门大小,0 = 16-bit, 1 = 32-bit
uint8_t descriptor_type : 1; // 描述符类型,0 = 系统描述符,1 = 代码或数据描述符,
// 对于中断描述符来说,这个字段必须为0
uint8_t dpl : 2; // 描述符特权级(Descriptor Privilege Level)
uint8_t p : 1; // Segment Present Flag
};
};
uint16_t base_hi; // 中断服务例程的高16位,task gate没有这个字段
} __attribute__((packed));
中断链
need to be improved
上下文恢复
need to be improved