1 基本概念
中断定义:通常被定义为改变CPU指令执行序列的事件。
中断可以分为异步和同步中断:
- 同步中断,是由CPU在执行指令时由CPU控制单元产生的中断。这意味着,CPU必须停止指令的执行,转而响应中断。通常情况下,同步中断就是指 异常。
- 异步中断,是由外部设备随机产生的,信号采样按照CPU时钟信号。异步中断就是我们通常情况下所指的中断。一般是定时器中断和I/O设备中断。
异常通常分为2类:一类是编程错误,另外一类就是需要内核处理的异常情况。编程错误,比如程序异常终止,处理这种异常,内核只需要给当前进程发送一个信号即可。而需要内核处理的异常,比如页错误、通过汇编语言指令比如int或sysenter等请求内核服务等,需要内核作出相应的处理。
2 中断信号的作用
顾名思义,中断信号提供了一种使CPU跳出当前正在执行的代码流的方法。这是对于轮询机制的一种有效补充,中断机制提高了系统效率。当中断信号来临时,CPU停止当前正在执行的指令,保存当前进程内核态栈中的PC寄存器值(例如,eip和cs寄存器),取而代之的是,将中断类型相关的地址写入到PC寄存器中,从而切换到新的中断上下文。
但是,需要注意的是中断处理和进程切换有着很大不同:中断或者异常处理程序不是进程。它的处理完全在内核态,而且处理的内容非常少,要求响应时间必须非常短。
中断处理对时间高度敏感,有着严格要求:
- 因为中断随时发生,打断内核的调度。因此,内核希望尽快处理完中断,然后回到正常的调度执行中。比如,假设从网络上接收一个数据包,硬件中断内核,标记数据已经接收,然后就把CPU的使用权交还给之前正在运行的任务。稍后,由负责数据接收的进程来搬运数据到缓冲区,并作进一步处理。由此可见,响应中断的任务就被分成了两部分:紧急部分,由内核立即处理;可延时处理部分留给其它任务处理。
- 因为中断会随时发生,有时候,内核正在处理一个中断的时候,另一个中断可能会发生。中断处理程序必须能够允许中断嵌套处理。
- 虽然内核允许中断嵌套处理,但是内核代码中,必须提供临界段代码,在其中,中断必须被禁止。因为有些时候,我们的代码是不允许被中断的,这也是内核同步的一种手段。
3 中断和异常
Intel官方文档将中断和异常分类为:
- 中断:
- 可屏蔽中断 所有I/O设备发出的IRQ都能产生可屏蔽中断。屏蔽掉的中断,中断控制器忽略其存在。
- 非可屏蔽中断 只有很少的重要事件会产生非屏蔽中断。比如,硬件错误。非屏蔽中断总是能够被硬件识别。
- 异常:
- Fault 这类异常可以纠正。因为这类错误就是eip指令造成的,所以,一旦异常处理程序正确处理异常后,就可以继续执行eip寄存器中的指令了。
- Trap 陷阱指令造成的异常。陷阱同Fault一样,因为没有破坏内核态栈中的任何东西,异常处理程序终止后,可以继续执行eip寄存器中的指令。它的设计目的主要是为了调试,告知调试器正在执行一个特殊的指令(比如,在程序里打一个断点)。一旦用户查看完断点处信息后,他就可以让程序继续执行了。
- Abort 发生严重错误时的异常。此时,CPU控制单元发生异常,但是无法确定发生错误的指令的准确位置,也就是说,在eip寄存器中的指令并不是造成错误的指令。这类错误一般是硬件错误或系统页表中非法或者不一致的地址等。控制单元发出信号,让CPU跳转到异常处理程序。Abort异常处理程序一般都是终止程序的执行。
- 处理器检测异常 当CPU在执行指令时,检测出的异常。依赖于异常发生时,内核态栈中的eip寄存器指令,又可以分为三类:
- 编程异常 这类异常一般是由程序员故意造成的。可以使用int或int3指令触发,也可以使用into-溢出中断指令和bound-地址限制异常中断指令检查相应的条件,如果条件为假,也会产生异常。可编程错误一般被当作陷阱-trap处理,通常被称为软件中断。这类异常一般有两种作用:系统调用和告知调试器某个事件。
中断或异常使用一张中断向量表进行管理,编号为0-255。非可屏蔽中断和异常编号是固定的;而可屏蔽中断是不固定的,可以通过对中断控制器进行编程进行修改。
4 中断请求线-IRQ
硬件设备和可编程中断控制器之间使用中断请求线(IRQ)进行连接。中断请求线用来传输电信号。可编程中断控制器接收这些电信号,然后将其转换成中断号。具体如下:
- 监听IRQ线,检查上升沿信号。如果同时检测到多个信号,选择数字小的IRQ线。
- 如果IRQ中断请求线上产生一个上升沿信号:
- 将电信号转换成对应的中断向量,说白了,就是转换成一个对应的数字;
- 将该向量存储到中断控制器的I/O端口,然后允许CPU通过数据总线读取它;
- 发送一个中断信号到INTR管脚;
- 等待CPU应答该中断信号,然后,清除INTR中断线。
- 回到第1步。
IRQ线一般从0开始编号,也就是说,第一条IRQ线标记为IRQ0。Intel默认的中断向量表是从IRQ32开始的,也就是说,前32个中断号已经被不可屏蔽中断和保留中断所占用。IRQ线和向量表之间的对应关系可以通过给中断控制器发送合适的I/O指令进行修改。
可以通过对可编程中断控制器(PIC)进行编程,控制是否为某个指定的IRQ线发送中断。被禁止的中断不会丢失,一旦重新使能,PIC就会把它们发送给CPU。大部分的中断处理程序都使用这个特性,可以连续地处理相同类型的IRQ请求。
需要注意的是,这里的使能、禁止IRQ和中断的屏蔽不是一个概念。当eflag寄存器中的IF标志被清除,PIC发送的可屏蔽中断会被CPU忽略掉。cli和sti汇编指令分别用于清除和设置该标志。
传统的PIC控制器使用2个8259A外部芯片,通过”级联”的方式组成的。每个芯片可以接收8个不同的IRQ,但是从PIC的INT输出管脚连接到主PIC的IRQ2管脚上,所以总的可以使用的IRQ线就是15个。