中断(interrupt)通常被定义为一个事件,该事件改变处理器执行的指令顺序.这样的事件与CPU芯片内外部硬件电路产生的电信号相对应.
中断通常分为同步(synchronous)中断和异步(asynchronous)中断:
异常是由程序的错误产生的,或者是由内核必须处理的异常条件产生的.第一种情况下,内核通过发送一个每个Unix程序员都熟悉的信号来处理异常.第二种情况下,内核执行恢复异常需要的所有步骤,例如缺页
中断是由间隔定时器和I/O设备产生的,例如,用户的一次按键会引起一个中断
Intel文档把中断和异常分为以下几类:
中断
可屏蔽中断是指可以被程序通过设置中断屏蔽位(Interrupt Mask)来阻止的中断.这类中断通常是由外部设备(如键盘、鼠标、磁盘驱动器等)产生的,它们在某些情况下可能不需要立即处理.可屏蔽中断允许操作系统或程序选择性地忽略这些中断,以便集中处理更重要的任务
例如I/O 完成中断, 硬盘读取数据完成后发出的中断可以通过设置中断屏蔽位来暂时忽略,直到系统准备好处理这些数据.
不可屏蔽中断是指不能被程序屏蔽的中断,它们通常用于处理紧急情况,如硬件故障、电源故障或其他关键事件.由于这些中断不能被程序控制,它们具有最高的优先级,确保了系统能够立即响应这些关键事件.
例如硬件故障中断, 如内存错误、电源故障等,这些中断通常由硬件直接产生,不经过操作系统的中断控制器.
异常
故障是指程序执行过程中由于某种错误而导致的异常, 通常可以纠正;一旦纠正,程序就可以在不失连贯性的情况下重新开始.保存在eip中的值是引起故障的指令地址,因此,当异常处理程序终止时,那条指令会被重新执行.故障可以被操作系统捕获并处理,以恢复程序的正常执行或终止程序.
例如页面错误(Page Fault):当程序试图访问一个未映射到物理内存的虚拟内存地址时,会触发页面错误.操作系统需要处理这个异常,可能涉及加载缺失的内存页或终止程序
保护故障(Protection Fault):当程序试图访问它没有权限的内存区域时,会触发保护故障.例如,用户模式下的程序试图访问内核模式的内存.
陷阱是一种由程序自身生成的异常,通常用于系统调用.程序通过执行特定的指令(如在x86架构中的int指令)来请求操作系统提供的服务,如文件操作、网络通信等. 调试程序设置断点的时候也采用的这种异常处理.
异常终止是指程序由于某些严重错误而被迫终止执行.这类异常通常是由硬件或操作系统检测到的,表明程序无法继续执行.
例如硬件异常终止:如果硬件检测到严重错误,如总线错误或设备故障,可能会导致程序异常终止; 或者软件异常终止:程序逻辑错误或资源耗尽(如栈溢出)可能导致操作系统强制终止程序
编程异常是由程序中的编程错误引起的异常.这些错误可能是由于程序员的疏忽或逻辑错误导致的,它们可以在程序执行时被操作系统捕获.
例如 除零错误 或者 数组越界
当然有时我们也用术语"中断信号"来同时指这两种类型(中断和异常). 顾名思义,中断信号提供了一种特殊的方式,使处理器转而去运行正常控制流之外的代码.当一个中断信号达到时,CPU必须停止它当前正在做的事情,并且切换到一个新的活动.
为了做到这一点,就要在内核态堆栈保存程序计数器的当前值(例如 x86 中 eip和cs寄存器的内容),并把与中断类型相关的一个地址放进程序计数器.
中断处理是由内核执行的最敏感的任务之一,因为它必须满足下列约束:
例如,假设一个数据块已到达了网线,当硬件中断内核时,内核只简单地标志数据到来了,让处理器恢复到它以前运行的状态,其余的处理稍后再进行(如把数据移入一个缓冲区,它的接收进程可以在缓冲区找到数据并恢复这个进程的执行).因此,内核响应中断后需要进行的操作分为两部分:关键而紧急的部分,内核立即执行;其余推迟的部分内核随后执行.
中断处理程序与进程的上下文切换类似, 但有一个明显的差异:由中断或异常处理程序执行的代码不是一个进程.更确切地说,它是一个内核控制路径,代表中断发生时正在运行的进程执行. 作为一个内核控制路径,中断处理程序比一个进程要"轻"(light)(中断的上下文很少,建立或终止中断处理需要的时间很少).
每个中断和异常是由 0~255
之间的一个数来标识.在计算机体系结构中,尤其是在x86架构中,中断向量是一个用于标识特定中断或异常处理程序的数值.Intel将这个8位的无符号整数称为"向量",原因在于这些数值在中断描述符表(Interrupt Descriptor Table, IDT)中用作索引,以便处理器能够快速定位到相应的中断处理程序.
在中断处理的上下文中,每个中断向量指向IDT中的一个条目,该条目包含了处理特定中断的代码的地址.当发生中断时,处理器会使用中断向量作为索引,查找IDT中相应的条目,并跳转到该条目指向的中断处理程序执行.
非屏蔽中断的向量(2)和异常的向量(0-31)是固定的,而可屏蔽中断(32-255)的向量可以通过对中断控制器的编程来改变
/* Interrupts/Exceptions */
enum {
X86_TRAP_DE = 0, /* 0, Divide-by-zero */
X86_TRAP_DB, /* 1, Debug */
X86_TRAP_NMI, /* 2, Non-maskable Interrupt */
X86_TRAP_BP, /* 3, Breakpoint */
X86_TRAP_OF, /* 4, Overflow */
X86_TRAP_BR, /* 5, Bound Range Exceeded */
X86_TRAP_UD, /* 6, Invalid Opcode */
X86_TRAP_NM, /* 7, Device Not Available */
X86_TRAP_DF, /* 8, Double Fault */
X86_TRAP_OLD_MF, /* 9, Coprocessor Segment Overrun */
X86_TRAP_TS, /* 10, Invalid TSS */
X86_TRAP_NP, /* 11, Segment Not Present */
X86_TRAP_SS, /* 12, Stack Segment Fault */
X86_TRAP_GP, /* 13, General Protection Fault */
X86_TRAP_PF, /* 14, Page Fault */
X86_TRAP_SPURIOUS, /* 15, Spurious Interrupt */
X86_TRAP_MF, /* 16, x87 Floating-Point Exception */
X86_TRAP_AC, /* 17, Alignment Check */
X86_TRAP_MC, /* 18, Machine Check */
X86_TRAP_XF, /* 19, SIMD Floating-Point Exception */
X86_TRAP_IRET = 32, /* 32, IRET Exception */
};
每个能够发出中断请求的硬件设备控制器都有一条名为IRQ(Interrupt ReQuest)的输出线. 所有现有的IRQ线(IRQ line)都与一个名为可编程中断控制器(Programmable InterrptControuer.PIC)的硬件电路的输入引脚相连,可编程中断控制器执行下列动作:
IRQ线是从0开始顺序编号的,因此,第一条IRQ线通常表示成IRQ0.与IRQn关联的Intel向量是n+32.如前所述,通过向中断控制器端口发布合适的指令,就可以修改IRQ和向量之间的映射.可以有选择地禁止每条IRQ线.因此,可以对PIC编程从而禁止IRQ,也就是说,可以告诉PIC停止对给定的IRQ线发布中断,或者激活它们.禁止的中断是丢失不了的,它