系统中断
扫描二维码
随时随地手机看文章
中断机制是现代计算机系统中的基本机制之一,他完成了对计算机各个事件(如时钟、键盘等)响应工作。
首先,有两类事件能引起x86挂起当前指令流的执行并响应事件: 异常(Exception) 和 硬件中断(Interrupt)。其中异常又被称为 软中断,他用于处理计算机执行过程中的一些异常情况,如除0操作、溢出、栈错误等情况,这些中断 不可屏蔽。而硬件中断又分为了外部中断(可屏蔽)和内部中断(不可屏蔽),外部中断一般就是计算机外设所发出的中断(如敲击了键盘等),它们由如下图所示的两个级联的8259A芯片所控制 ;内部中断就是计算机内部硬件出现的像硬件出错(掉电、校验、传输)等情况所发出的中断。上述所有的这些中断,都被统一的编排在了接下来将介绍的中断向量表中。
8086机器中的中断向量表在80386机器中被改为了中断描述符表(interrupt description table,IDT),它存放着用于描述各个中断的表项,每个都由8字节组成,包括了该中断的地址、特权级等信息。这些表项又被称为了门描述符(Gate Descriptor),“门”的含义是当中断发生时必须先通过这些门,然后才能进入相应的中断处理程序(Interrupt Service Routine, ISR)。
如前文所述,所有的中断(或异常)都被 统一 的编排在了IDT中,即这个IDT包含了异常向量和硬件中断向量,而IDT中一共有256个门描述符,每个门描述符描述一个中断(或异常),同时就对应着一个中断(或异常)的处理程序。这256个中断或异常向量的分配如下:
* 从0~31 的向量对应于异常和非屏蔽中断;
* 从32~47 的向量(即由I/O 设备引起的中断)分配给屏蔽中断;
* 剩余的从48~255 的向量用来标识软中断。Linux 只用了其中的一个(即128 或0x80向量)用来实现系统调用(trap)。当用户态下的进程执行一条int 0x80 汇编指令时,CPU 就切换到内核态,并开始执行system_call() 内核函数。
我们先说异常的处理。在这256个处理程序中,前32个都是非屏蔽中断,0~20号中断处理程序又都用于CPU的异常中断(即软中断),随后的12个中断处理程序被Intel保留,详细情况如下:
// 0~31个都是非屏蔽中断
// 声明中断处理函数 0-19 属于 CPU 的异常中断
// ISR:中断处理程序(interrupt service routine)
void isr0(); // 0 #DE 除 0 异常
void isr1(); // 1 #DB 调试异常
void isr2(); // 2 NMI
void isr3(); // 3 BP 断点异常
void isr4(); // 4 #OF 溢出
void isr5(); // 5 #BR 对数组的引用超出边界
void isr6(); // 6 #UD 无效或未定义的操作码
void isr7(); // 7 #NM 设备不可用(无数学协处理器)
void isr8(); // 8 #DF 双重故障(有错误代码)
void isr9(); // 9 协处理器跨段操作
void isr10(); // 10 #TS 无效TSS(有错误代码)
void isr11(); // 11 #NP 段不存在(有错误代码)
void isr12(); // 12 #SS 栈错误(有错误代码)
void isr13(); // 13 #GP 常规保护(有错误代码)
void isr14(); // 14 #PF 页故障(有错误代码)
void isr15(); // 15 CPU 保留
void isr16(); // 16 #MF 浮点处理单元错误
void isr17(); // 17 #AC 对齐检查
void isr18(); // 18 #MC 机器检查
void isr19(); // 19 #XM SIMD(单指令多数据)浮点异常
// 20-31 Intel 保留
//...
其次再说硬件中断的处理。在前面的8259A芯片的示意图中,可能你已经发现,我们给每一个外部中断(可屏蔽中断)都赋予了一个编号,这16个编号被称作中断请求号码(Interrupt Request, IRQ)。它们实际上就是IDT中的32~47号向量,详细情况如下:
// IRQ 定义
// 定义IRQ
#define IRQ0 32 // 电脑系统计时器
#define IRQ1 33 // 键盘
#define IRQ2 34 // 与 IRQ9 相接,MPU-401 MD 使用
#define IRQ3 35 // 串口设备
#define IRQ4 36 // 串口设备
#define IRQ5 37 // 建议声卡使用
#define IRQ6 38 // 软驱传输控制使用
#define IRQ7 39 // 打印机传输控制使用
#define IRQ8 40 // 即时时钟
#define IRQ9 41 // 与 IRQ2 相接,可设定给其他硬件
#define IRQ10 42 // 建议网卡使用
#define IRQ11 43 // 建议 AGP 显卡使用
#define IRQ12 44 // 接 PS/2 鼠标,也可设定给其他硬件
#define IRQ13 45 // 协处理器使用
#define IRQ14 46 // IDE0 传输控制使用
#define IRQ15 47 // IDE1 传输控制使用
// 外部中断
// 声明 IRQ 函数
// IRQ:中断请求(Interrupt Request)
void irq0(); // 电脑系统计时器
void irq1(); // 键盘
void irq2(); // 与 IRQ9 相接,MPU-401 MD 使用
void irq3(); // 串口设备
void irq4(); // 串口设备
void irq5(); // 建议声卡使用
void irq6(); // 软驱传输控制使用
void irq7(); // 打印机传输控制使用
void irq8(); // 即时时钟
void irq9(); // 与 IRQ2 相接,可设定给其他硬件
void irq10(); // 建议网卡使用
void irq11(); // 建议 AGP 显卡使用
void irq12(); // 接 PS/2 鼠标,也可设定给其他硬件
void irq13(); // 协处理器使用
void irq14(); // IDE0 传输控制使用
void irq15(); // IDE1 传输控制使用
这样,用户程序就可通过INT指令加一个中断向量向系统发出一个中断请求了,如DOS汇编指令中使用 INT 21 就能调用功能丰富的21号中断服务。