中断与异常
一、中断(广义):会改变处理器执行指令的顺序,通常与CPU芯片内部或外部硬件电路产生的电信号相对应
- 中断——异步的:由硬件随机产生,在程序执行的任何时候可能出现
- 异常——同步的:在(特殊的或出错的)指令执行时由CPU控制单元产生
- 我们用“中断信号”来通称这两种类型的中断
二、中断信号的作用
- 中断信号提供了一种特殊的方式,使得CPU转去运行正常程序之外的代码
- 当一个中断信号到达时,CPU必须停止它当前正在做的事,并且切换到一个新的活动
- 在进程的内核态堆栈保存程序计数器的当前值(即eip和cs寄存器)以便处理完中断的时候能正确返回到中断点
- 并把与中断信号相关的一个地址放入进程序计数器,从而进入中断的处理
三、中断信号的处理原则
- 内核的目标就是让中断尽可能快的处理完,尽其所能把更多的处理向后推迟
- 允许不同类型中断的嵌套发生,这样能使更多的I/O设备处于忙状态
- 尽管内核在处理一个中断时可以接受一个新的中断,但在内核代码中还在存在一些临界区,在临界区中,中断必须被禁止
四、中断上下文
- 中断上下文不同于进程上下文
- 中断或异常处理程序执行的代码不是一个进程。它是一个内核控制路径,代表了中断发生时正在运行的进程执行
- 作为一个进程的内核控制路径,中断处理程序比一个进程要“轻”(中断上下文只包含了很有限的几个寄存器,建立和终止这个上下文所需要的时间很少)
五、由于Linux不为中断处理程序设置process context,A只能使用 C的kernel stack作为自己的运行栈
- 无论如何,Linux的interrupt context A绝对不会被某个进程C或者D抢占!!!
- 这是由于所有已经启动的interrupt contexts,不管是interrupt contexts之间切换,还是在某个interrupt context中执行代码的过程,决不可能插入scheduler调度例程的调用。
- 除非interrupt context主动或者被动阻塞进入睡眠,唤起scheduler,但这是必须避免的,危险性见第3点说明
- 首先,interrupt context没有process context,A中断是“借”了C的进程上下文运行的,若允许A“阻塞”或“睡眠”,则C将被迫阻塞或睡眠,仅当A被“唤醒”C才被唤醒;而“唤醒”后,A将按照C在就绪队列中的顺序被调度。这既损害了A的利益也污染了C的kernel stack。
- 其次,如果interrupt context A由于阻塞或是其他原因睡眠,外界对系统的响应能力将变得不可忍受
六、那么interrupt context A和B的关系又如何呢?
- 由于可能在interrupt context的某个步骤打开了CPU的IF flag标志,这使得在A过程中,B的irq line已经触发了PIC,进而触发了CPU IRQ pin,使得CPU执行中断B的interrupt context,这是中断上下文的嵌套过程。
- 通常Linux不对不同的interrupt contexts设置优先级,这种任意的嵌套是允许的
- 当然可能某个实时Linux的patch会不允许低优先级的interrupt context抢占高优先级的interrupt context
I/O设备如何引起CPU中断
七、中断分为:
- 可屏蔽中断(Maskable interrupt)
- I/O设备发出的所有中断请求(IRQ)都可以产生可屏蔽中断。
- 可屏蔽中断可以处于两种状态:屏蔽的(masked)和非屏蔽的(unmasked)
- 非屏蔽中断(Nonmaskable interrupt)
- 只有几个特定的危急事件才引起非屏蔽中断。如硬件故障或是掉电