I/O 中断原理
本篇文章会对中断操作的原理进行说明。
什么是中断
中断指当出现需要时,CPU 暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。即在程序运行过程中,系统出现了一个必须由 CPU 立即处理的情况,此时,CPU 暂时中止程序的执行转而处理这个新的情况的过程就叫做中断。
我们知道 CPU 是按指令顺序进行执行的,操作系统每过大约 15ms 会发生一次线程调度(Windows 下),根据线程优先级先调度优先级高的线程。但是实际情况并没有那么简单,若我们接收到一个网络请求,如果要等当前线程执行完或 15ms 线程调度之后才去处理网络请求,网卡缓冲区很有可能会被占满,此时就发生了丢包。
中断类型
中断分为硬件中断和软件中断。
硬件中断
硬件中断即为硬件发出的中断信号,如 I/O 中断和硬件失效中断。
- I/O 中断:由 I/O 控制器产生,用于发送信号通知操作完成等信号。
- 硬件失效中断:如掉电或存储器奇偶错之类的故障。
软件中断
软件中断即为非硬件发出的中断信号,如程序中断和时钟中断。
- 程序中断:一些指令产生的异常(如算数移除、除数为 0 等)。
- 时钟中断:由处理器内部的计时器产生,允许操作系统以一定规程执行函数。
我们提到了操作系统每过大约 15ms 会进行一次线程调度,就是利用时钟中断来实现的。
I/O中断流程
本篇文章还是主要解释前几篇文章提到的 I/O 中断进行解释说明,因此仅以 I/O 中断举例,但是中断的原理和流程都是相似的。
I/O 中断通过中断处理器执行中断操作。当外部设备的 I/O 模块准备好时,它会发送给 CPU 一个中断信号,CPU 则会 “ 立即 ” 做出响应,暂停当前程序的处理去服务该 I/O 设备的程序。
也可能不是立即,比如同时存在多个中断,则根据实际的中断算法决定,是按中断先后顺序执行中断操作,还是按中断优先级执行。
I/O 中断时硬件中断,需要硬件支持来接收中断信号。
无中断
为了更好的说明中断带来的性能提升,我们先描述一下没有中断时程序如何处理 I/O 操作。
- 当我们程序需要从硬盘读取一个文件时,会先检查内核缓存中是否有数据,若没有数据,则执行实际 I/O 操作。在 I/O 操作执行时,我们的用户线程将阻塞等待数据从硬盘写到内存中。对于用户来说线程是被阻塞的。
- 在实际的 I/O 操作过程中,若没有中断操作,CPU 会不断轮询检查 I/O 操作是否完成,若 I/O 操作没有完成则继续调度其他线程,过一会儿再来检查。若操作完成,CPU 将线程加入到线程就绪队列中并恢复线程上下文信息。
- 线程处于就绪队列,可以被操作系统调度从而继续执行读操作,此时会将数据从操作系统内核缓存读取到用户缓存中。
有中断
- 当我们程序需要从硬盘读取一个文件时,会先检查内核缓存中是否有数据,若没有数据,则执行实际 I/O 操作。在 I/O 操作执行时,我们的用户线程将阻塞等待数据从硬盘写到内存中。对于用户来说线程是被阻塞的。
- 在实际的 I/O 操作过程中,CPU 向 I/O 模块(DMA 控制器)发送读指令,然后就去调度其他线程。
- 当 I/O 模块(DMA 控制器)I/O 执行完成后,会产生中断信号在通知 CPU,CPU 将线程加入到线程就绪队列中并恢复线程上下文信息。
- 线程处于就绪队列,可以被操作系统调度从而继续执行读操作,此时会将数据从操作系统内核缓存读取到用户缓存中。
由此可知,有中断还是没有中断对于用户来说线程都是阻塞的,对于操作系统内核来说通过中断方式主动通知 CPU 的方式减少了线程轮询判断,提高了线程执行效率。
当然,为了进一步提高线程利用率,此时我们可以通过异步操作 API 执行 I/O 操作。
比如 .Net Framework 4.5 的 async
和 await
关键字,当调用异步操作后,API 内部保存了相关状态机信息(回调信息),线程继续执行其他操作,当操作系统内核读取数据完成时,线程调用回调方法恢复到 await
的后续操作。整个过程中线程不会因为阻塞带来导致性能损失。
中断处理
当 I/O 设备完成一次 I/O 操作时,发生以下事件:
- 开始 I/O 操作前,处理器将当前处理的相关信息如指令地址、必要的状态信息等保存到栈中,使得中断后可以恢复执行。
- I/O 操作完成后,设备给处理器发送一个中断信号。
- 处理器响应中断信号。
- 处理器对中断信号进行判断,若存在未响应的中断,则给产生中断信号的设备发送确认信号,确认信号使得设备取消它的中断信号。
- 处理器将控制前转移给中断程序中,中断程序从栈中获取之前保存的信息,使得能继续执行 I/O 完成时的后续操作。
- 处理器将中断程序入口地址载入到程序计数器中,使得处理器能继续执行下一个指令周期。
相关文献
- 《操作系统 - 精髓与设计原理》
- 时钟中断是 rt-thread 的线程调度器的驱动力