聊聊中断

中断基本概念

什么是中断?

中断,即打断CPU的运行。具体来说,在计算机运行过程中,当发生某个事件后,CPU 会停止当前程序流,转而去处理该事件,并在处理完毕后继续执行原程序流。

正式一点来说, 中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理。

中断分类

中断可分为同步(synchronous)中断和异步(asynchronous)中断:

  1. 同步中断是当指令执行时由 CPU 控制单元产生,之所以称为同步,是因为只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间,比如系统调用。
  2. 异步中断是指由其他硬件设备依照 CPU 时钟信号随机产生,即意味着中断能够在指令之间发生,例如键盘中断。

其中同步中断通常为软件中断,异步中断通常是硬件中断。硬件中断通常还包括课评比中断、非可屏蔽中断、处理器中断等。

从广义上讲,中断可以分为四类:中断、故障、陷阱、终止。

类别 原因 异步/同步 返回行为
中断 来自I/O设备的信号 异步 总是返回到下一条指令
陷阱 有意的异常 同步 总是返回到下一条指令
故障 潜在可恢复的错误 同步 返回到当前指令
终止 不可恢复的错误 同步 不会返回

中断的作用

操作系统需要对连接到计算机上的所有硬件设备进行管理,要管理这些设备,首先得和它们互相通信才行,一般有两种方案可实现这种功能:

  • 轮询(polling) 让内核定期对设备的状态进行查询,然后做出相应的处理;
  • 中断(interrupt) 让硬件在需要的时候向内核发出信号(变内核主动为硬件主动)。
    第一种方案会让内核做不少的无用功,因为轮询总会周期性的重复执行,大量地耗用 CPU 时间,因此效率及其低下,所以一般都是采用第二种方案 。

中断机制的好处是 化主动为被动,避免 CPU 轮询等待某条件成立。如果没有中断机制,那么“某个条件成立”就需要 CPU 轮询判断,这样就会增加系统的开销。而使用中断机制,就可以在条件成立之后,向 CPU 发送中断事件,强制中断 CPU 执行程序,转而去执行中断处理程序。

中断(异常)处理

需要明确的一点是CPU对于中断和异常的具体处理机制本质上是完全一致的,即:

当CPU收到中断或者异常的信号时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理程序中,在完成对这个信号的处理后再跳回到刚才被打断的程序或任务中。这里只描述保护模式下的处理过程,搞清楚了保护模式下的处理过程(更复杂),实模式下的处理机制也就容易理解了。

具体的处理过程如下:

  1. 中断响应的事前准备:

系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序具体是如何工作的。系统只有事前对这两件事都知道得很清楚,才能正确地响应各种中断信号和异常。

  • 系统将所有的中断信号统一进行了编号(一共256个:0~255),这个号称为中断向量,具体哪个中断向量表示哪种中断有的是规定好的,也有的是在给定范围内自行设定的。 中断向量和中断服务程序的对应关系主要是由IDT(中断向量表)负责。操作系统在IDT中设置好各种中断向量对应的中断描述符(一共有三类中断门描述符:任务门、中断门和陷阱门),留待CPU查询使用。而IDT本身的位置是由idtr保存的,当然这个地址也是由OS填充的。

  • 中断服务程序具体负责处理中断(异常)的代码是由软件,也就是操作系统实现的,这部分代码属于操作系统内核代码。也就是说从CPU检测中断信号到加载中断服务程序以及从中断服务程序中恢复执行被暂停的程序,这个流程基本上是硬件确定下来的,而具体的中断向量和服务程序的对应关系设置和中断服务程序的内容是由操作系统确定的。

  1. CPU检查是否有中断/异常信号

    CPU在执行完当前程序的每一条指令后,都会去确认在执行刚才的指令过程中中断控制器(如:8259A)是否发送中断请求过来,如果有那么CPU就会在相应的时钟脉冲到来时从总线上读取中断请求对应的中断向量。

    对于异常和系统调用那样的软中断,因为中断向量是直接给出的,所以和通过IRQ(中断请求)线发送的硬件中断请求不同,不会再专门去取其对应的中断向量。

  2. 根据中断向量到IDT表中取得处理这个向量的中断程序的段选择符

    CPU根据得到的中断向量到IDT表里找到该向量对应的中断描述符,中断描述符里保存着中断服务程序的段选择符。

  3. 根据取得的段选择符到GDT中找相应的段描述符

    CPU使用IDT查到的中断服务程序的段选择符从GDT中取得相应的段描述符,段描述符里保存了中断服务程序的段基址和属性信息,此时CPU就得到了中断服务程序的起始地址。这里,CPU会根据当前cs寄存器里的CPL和GDT的段描述符的DPL,以确保中断服务程序是高于当前程序的,如果这次中断是编程异常(如:int 80h系统调用),那么还要检查CPL和IDT表中中断描述符的DPL,以保证当前程序有权限使用中断服务程序,这可以避免用户应用程序访问特殊的陷阱门和中断门。

  4. CPU根据特权级的判断设定即将运行的中断服务程序要使用的栈的地址

    CPU会根据CPL和中断服务程序段描述符的DPL信息确认是否发生了特权级的转换,比如当前程序正运行在用户态,而中断程序是运行在内核态的,则意味着发生了特权级的转换,这时CPU会从当前程序的TSS信息(该信息在内存中的首地址存在TR寄存器中)里取得该程序的内核栈地址,即包括ss和esp的值,并立即将系统当前使用的栈切换成新的栈。这个栈就是即将运行的中断服务程序要使用的栈。紧接着就将当前程序使用的ss,esp压到新栈中保存起来。也就说比如当前在某个函数中,使用的栈,在中断发生时,需要切换新的栈。

  5. 保护当前程序的现场

    CPU开始利用栈保护被暂停执行的程序的现场:依次压入当前程序使用的eflags,cs,eip,errorCode(如果是有错误码的异常)信息。

    官方文档给出的栈变化的示意图如下:

  6. 跳转到中断服务程序的第一条指令开始执行

    CPU利用中断服务程序的段描述符将其第一条指令的地址加载到cs和eip寄存器中,开始执行中断服务程序。这意味着先前的程序被暂停执行,中断服务程序正式开始工作。

  7. 中断服务程序处理完毕,恢复执行先前中断的程序

    在每个中断服务程序的最后,必须有中断完成返回先前程序的指令,这就是iret(或iretd)。程序执行这条返回指令时,会从栈里弹出先前保存的被暂停程序的现场信息,即eflags,cs,eip重新开始执行。

常见术语

  1. 内中断: 即程序运行错误引起的中断
  2. 外中断: 即由外部设备、接口卡引起的中断
  3. 软件中断: 由写在程序中的语句引起的中断程序的执行,称为软件中断
  4. 异常:异步中断或者叫软中断
  5. 系统调用:是一种软中断处理程序,用于让程序从用户态陷入内核态,以执行相应的操作
  6. 中断号: 由硬件(通常是中断控制器)产生,以标识不同的中断源
  7. 中断向量:中断服务程序的入口地址
  8. 中断向量表:存放中断向量的一片内存区
  9. 中断服务程序:处理中断的程序

参考文献

  1. https://developer.aliyun.com/article/23491
  2. https://www.cnblogs.com/aaronLinux/p/10842499.html
  3. https://www.jianshu.com/p/4efd858c6520
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信