图解:操作系统-中断处理

392 阅读8分钟

什么是中断

中断是计算机系统中一种重要的机制,他可以让cpu先停下手头的工作,转而处理需要优先应对的工作,处理完成后再恢复原本的处理流程

这套机制使得 CPU 可以高效地响应外部设备的请求(如键盘、鼠标、磁盘 I/O 等),而不必轮询(Polling)设备状态

中断流程

我们先用大白话简述这个流程

首先,外部设备有和CPU沟通的需求,比如键盘打字,这时候键盘会发出中断请求(IRQ),让cpu先处理打字显示的事件

然后,键盘发出的中断请求IRQ会被一个叫做中断控制器(PIC)的家伙收集,通知CPU处理中断并传给他中断向量(有时候也叫做中断号),告诉CPU需要处理那个中断事件

接下来CPU会拿着中断向量去查一个叫做IDT的表,弄明白这个中断到底是要我干啥的

CPU执行完中断后再告诉PIC,说哥们我done了,您看着办吧。然后PIC,CPU就各干各的

接下来,我简单介绍一下其中几个概念

概念

8259 PIC

PIC,中断控制器,负责管理硬件中断,并将它们发送到适当的系统中断处理程序中

先让我们看看这哥们的英容

image.png

额,有点丑陋,但不要紧。上图是两块PIC组合在一起,一块是主PIC,另一块是从PIC

接下来,为了讲述方便,笔者只考虑一个PIC的情况

在现代系统中,两个 8259 可编程PIC 各自有 8 个输入(IRQ 线)。当PIC发现某个输入线被外部设备摸了,这就表明某个输入需要 CPU处理中断

image.png


接下来,PIC会检查这个通道有没有被拉黑(被屏蔽了),以及目前CPU是否在处理其他中断,如果是,PIC就不会鸟他,否则就会拉高中断线,通过INT引脚发送INT信号(中断信号),告诉CPU说来活了,处理中断去

image.png

PIC组成部分

image.png

IMR

(Interrupt Mask Register)IMR,PIC内部的一个寄存器,这哥们就是用来拉黑某个输入通道的。IMR是个8 bit的 bitmap,刚好能和8个input对上。一旦这哥们某个bit置为1,就表示对应的input被拉黑了,被屏蔽了

对于那些被屏蔽的输入,PIC就会忽略它,该干嘛干嘛

下面是一段从wiki.osdev.org/上摘下来的代码,这段代码提供一个demo,表示如何为IMR设置掩码,清除掩码

image.png

IRQ_set_mask接受一个IRQline的入参,表示需要将哪个input给屏蔽

接下来,函数会判断IRQline的大小,用于判断需要屏蔽的是主从那块PIC的input

接下来函数会调用inb(port)函数,得到当前IMR的bitmap值,或上1 << IRQline,表示将对应位屏蔽

这里笔者额外多一嘴,说说啥事端口

如果是在web领域,端口就是用于区分同一台计算机上的不同服务或进程。比如常见的Tomcat默认跑在8080Mysql跑在3306

但在硬件领域,我认为端口就是CPU和硬件设备进行数据交换的地址。比如0x01,0x02...

IRR, ISR

IRR、ISR是PIC内部的两个中断状态寄存器

ISR(In-Service-Register),存储正在被处理的中断请求(IRQ),也就是那个被PIC发送给CPU的中断请求IQR

IRR(Interupt request Register),存储外部设备产生了哪些中断请求

在整个工作流程中,PIC会基于IMR屏蔽的输入信号,从IRR中选出最高优先级的中断请求IQR发送给CPU,当接收到CPU的回执后,PIC会把IRR中发送给CPU的那条IQR移动到ISR中,表示这条中断请求正在被执行,锁定整个流程

INT,INTA引脚

image.png

PIC和CPU交互,依赖于INT,INTA引脚

当接收到外部设备的中断请求后,PIC会拉高INT引脚,向CPU发出INT信号,通知CPU处理中断

随后,CPU会拉低INTA引脚,表示已收到请求,PIC老哥请锁定最高优先级的IRQ中断请求

IDT表

PIC向CPU发出中断请求后,会通过数据总线将中断向量传递给CPU

CPU获取中断向量后会查表弄明白自己要干啥

而这个表,就是IDT中断描述符表

这张表最大的用处是让CPU找到处理中断的程序——ISR,请注意,此处的ISR全称是Interrupt Service Routine,这点需要和PIC的ISR做出区分

image.png

IDT我们可以把它理解成是一个数组,里面存储着各式各样的Entry。每个Entry中存储不同的信息,用于指定CPU该干啥。并且每个Entry都对应着一个中断向量

IDTR

啥是IDTR?这玩意儿就是个存储查询IDT表基地址和Table Limit。其具体结构如下图所示

image.png

image.png

我们不搞这么复杂,就记住这玩意儿存储了个偏移地址就行

PIC通过中断向量查询IDT表时,需要加上IDTR存储的基地址,才能获取真正的IDT表对应的内容

有关IDTR的详细信息我就不多BB了,感兴趣的读者瞅两眼下面的图片吧,桀桀

寻址

CPU希望通过PIC传来的中断向量,通过IDT表查询到CPU需要干啥

那么问题来了,IDT表在哪里呢?CPU如何通过寻址找到中断向量对应的IDT表的Entry呢?

这里,给出寻址公式

address = IDT Base Address + 中断向量 * Descriptor Entry size + IDT Limit

image.png

看不懂?没关系,不需要看懂,又不用考试,看懂干啥呢?

反正就是通过IDTR + 中断向量算出来的

IDT表的Entry结构

image.png

看不懂?我也看不懂,所以我问deepseek,让这哥们帮我总结一下

总结:IDT 条目存储的核心内容

用一句话概括:
IDT 条目存储了中断处理程序的地址、调用方式(类型)、权限要求(特权级别)以及是否有效(存在位)。


简单类比

可以把 IDT 条目想象成一个“电话簿”:

  • 偏移地址 + 段选择子 :相当于电话号码,告诉 CPU 哪里能找到处理程序。
  • 类型 :相当于说明这通电话是普通电话(中断门)、紧急电话(陷阱门)还是特殊任务电话(任务门)。
  • 特权级别 :相当于说这通电话谁可以打(内核态还是用户态)。
  • 存在位 :相当于说这个电话号码是否有效。

哈哈,这下就清楚多了

中断服务程序ISR

CPU接收到中断后,会执行中断服务程序ISR,用于处理相应中断

ISR的存储地址则被记录在IDT表中

当CPU准备执行ISR前,他需要记录当前执行的状态。CPU会把EFLAGS -> CS -> EIP按照顺序压入

有关寄存器功能介绍,我扒了amd手册上的内容,具体如下

EFLAGS: 32-bit (extended) flags register.

EIP: 32-bit (extended) instruction-pointer register.

CS: Code segment register.

当CPU执行完成ISR后,会向PIC发送EOI(End Of Interrupt)指令,然后从栈中恢复被中断的程序,就像无事发生那样继续执行原本应该执行的代码

ISR由CPU直接调用,因此能够快速响应硬件的中断请求。如果这个过程卡顿,那么你绝对会气到敲键盘,同时发现显示器没有屁点响应

详细流程

介绍完这些必要概念后,我们把中断的整体流程串联,来一波详细介绍

PS: 我会额外增添一些细节,这些内容就不再上文详细介绍了,不然写起来太操蛋了

阶段1:PIC 通知 CPU(INT 信号激活)

  1. 外设(如键盘)触发中断,拉高对应的 IRQ 线

  2. 8259 PIC 检测到 IRQ 信号后:

    • 在 IRR(Interrupt Request Register)  中存储该IRQ信号,当个备胎
    • 如果该中断未被 IMR(Interrupt Mask Register)  拉黑,且优先级最高,则 PIC 拉高 INT 引脚
  3. CPU 在每个指令周期结束时检查 INTR 引脚

    • 如果 EFLAGS.IF=1(中断允许),CPU 准备响应
    • 如果 EFLAGS.IF=0(例如执行了 CLI 指令),CPU 忽略中断

阶段2:CPU 响应中断(INTA 周期)

CPU 通过 两个连续的 INTA 周期 与 PIC 交互:

第一个 INTA 周期
  • CPU 拉低 INTA 引脚,表示:"哥们我收到你的中断请求,把当前的IRQ给锁定了"

  • PIC 收到后:

    • 把刚刚发给CPU的IRQ从IRR里头转移到 ISR(In-Service Register) (表示该中断正在被处理)
    • 锁定其他低优先级中断(防止抢占)
第二个 INTA 周期
  • CPU 再次拉低 INTA 引脚,表示:"PIC老哥给我中断向量"
  • PIC 将 中断向量(如键盘中断是 0x21)放到 数据总线(D0-D7)  上
  • CPU 从数据总线读取该中断号

阶段3:CPU 处理中断

  1. CPU 根据中断号查询 IDT(Interrupt Descriptor Table) ,找到对应的中断处理程序(ISR)

  2. CPU 保存当前状态(EFLAGS、CS、EIP)到栈,并跳转到 ISR。

  3. ISR 执行完毕后,需向 PIC 发送 EOI(End of Interrupt)命令

  4. PIC 收到 EOI 后,清除 ISR 中的对应位,允许新中断。