中断控制器:8259A可编程中断控制器

说到中断,就绕不开8259A这个老伙计。我刚开始接触x86系统时,看到这块芯片的引脚图就头大——密密麻麻的,跟蜘蛛网似的。但后来真正用起来才发现,它的设计其实很巧妙。

8259A,全称是Intel 8259A Programmable Interrupt Controller。说白了,它就是CPU和外部中断源之间的一个「调度员」。CPU只有一个INTR引脚,但外部设备可能有十几个、几十个。怎么办?让8259A来管。

8259A的核心功能

我个人习惯把8259A的功能归纳为三点:

  • 中断请求的接收与转发——它最多能管理8个中断源(级联后更多)
  • 中断优先级的裁决——谁更紧急,谁先上
  • 中断向量的提供——告诉CPU该去查哪个中断服务程序

嗯,这里要注意:8259A不是简单的「谁先来谁先上」。它有一套优先级规则,而且你可以通过编程来改变这些规则。这就是「可编程」三个字的含义。

中断优先级:谁更紧急?

8259A支持几种优先级模式。我挑最常用的两种说说:

模式 说明 典型场景
固定优先级 IR0最高,IR7最低,固定不变 简单系统,优先级明确
循环优先级 刚服务完的中断变成最低优先级 多个设备优先级需要轮转

我在项目中遇到过一个问题:一个数据采集系统里,有8个传感器通过8259A上报数据。按固定优先级,IR0上的传感器总是优先被处理,IR7上的数据经常被丢掉。后来改成循环优先级,问题就解决了。你想想看,如果所有传感器都同等重要,固定优先级确实不合理。

优先级裁决的底层逻辑:

8259A内部有一个「中断服务寄存器(ISR)」和一个「中断请求寄存器(IRR)」。IRR记录谁在请求,ISR记录谁正在被服务。裁决时,8259A会比较当前请求的中断和正在服务的中断的优先级。如果新来的优先级更高,就打断当前服务——这叫「中断嵌套」。

中断屏蔽:不想理你的时候

有时候,某些中断源我们暂时不想处理。比如系统正在做关键操作,不想被键盘打断。这时候就需要中断屏蔽。

8259A有一个「中断屏蔽寄存器(IMR)」。你往IMR的某一位写1,对应的IR引脚就被屏蔽了。屏蔽后,那个设备发的中断请求,8259A直接忽略。

// 屏蔽IR3(键盘中断)
outb(0x21, 0x08);  // 往IMR写0x08,即bit3=1

// 解除屏蔽
outb(0x21, 0x00);  // 全部解除

我曾经犯过一个低级错误:在写一个嵌入式系统时,屏蔽了定时器中断,但忘了在退出临界区时恢复。结果系统时钟停了,所有依赖时间的任务都挂了。排查了整整两天才找到原因。嗯,从那以后我养成了一个习惯:屏蔽中断前先保存IMR的值,恢复时直接写回去,而不是写死值。

注意:中断屏蔽不是万能的。NMI(不可屏蔽中断)不受IMR控制。另外,屏蔽中断的时间要尽量短,否则可能丢失中断请求。

级联方式:一个不够,再来几个

一个8259A只能管8个中断源。但实际系统中,8个往往不够。怎么办?级联。

级联就是把多个8259A串起来。一个作为「主片」,其他的作为「从片」。主片的一个IR引脚接一个从片的INT输出。这样,一个从片又能管8个中断源。

举个例子:两个8259A级联,主片IR2接从片。那么系统能管理的中断源数量是:主片7个(IR0、IR1、IR3~IR7)+ 从片8个 = 15个。为什么主片只有7个?因为IR2被用来接从片了,不能再接外部设备。

级联的配置要点:

  • 主片和从片都要初始化,但配置不同
  • 主片需要知道哪个IR引脚接了从片
  • 从片需要知道自己的「身份ID」
  • 中断响应时,主片和从片要配合提供中断向量

我见过一个夸张的设计:用了9个8259A级联,管理64个中断源。说实话,这种设计现在已经很少见了。现代系统都用APIC(高级可编程中断控制器),功能更强,配置也更灵活。但8259A的思想——优先级、屏蔽、级联——这些概念到今天依然适用。

8259A的初始化流程

初始化8259A,说白了就是往它的控制端口写几个字节。我习惯把流程总结成四步:

  1. 写ICW1——告诉8259A要初始化了,顺便设置触发方式(边沿触发还是电平触发)
  2. 写ICW2——设置中断向量的基址。比如设成0x20,那么IR0对应中断向量0x20,IR1对应0x21,以此类推
  3. 写ICW3——如果是级联,这里配置主从关系
  4. 写ICW4——设置工作模式,比如是否使用8086模式、是否自动结束中断
// 初始化主片8259A(单片模式,8086系统)
outb(0x20, 0x11);  // ICW1: 边沿触发,需要ICW4
outb(0x21, 0x20);  // ICW2: 中断向量基址0x20
outb(0x21, 0x04);  // ICW3: 级联时用,这里单片,写0
outb(0x21, 0x01);  // ICW4: 8086模式,非自动EOI

说实话,这些初始化代码我写过无数遍,但每次写还是会去翻手册。因为不同系统、不同模式下,ICW的配置差别挺大的。我的建议是:别背,理解原理,用的时候查手册。

一张图看懂8259A

下面这张图是我画的8259A内部结构和工作流程。你看完应该能对它的运作有个整体印象。

8259A 可编程中断控制器结构图 IR0 IR1 IR2 IR3 IR4 IR5 IR6 IR7 IRR 中断请求寄存器 记录谁在请求 优先级 裁决器 比较优先级 ISR 中断服务寄存器 记录谁在服务 INT →CPU IMR 屏蔽 外部中断源 IRR IMR 优先级裁决 ISR

这张图里,IR0~IR7是外部中断源。它们的中断请求先进入IRR。IMR像一道闸门,被屏蔽的请求直接挡在外面。通过的请求进入优先级裁决器,裁决器比较优先级后,把最高优先级的请求送入ISR,同时向CPU发送INT信号。

嗯,这里有个细节:ISR里记录的是「正在被服务的中断」。如果新来的中断优先级更高,裁决器会让ISR暂停当前服务,先处理新的。这就是中断嵌套的硬件基础。


8259A虽然老了,但它的设计思想——分层管理、优先级裁决、可编程配置——这些理念在现代系统中依然能看到。你理解了8259A,再去看APIC、看ARM的GIC,会发现很多相似之处。说白了,中断控制器的本质没变,变的只是实现方式。

一句话总结:8259A就是CPU和外部中断源之间的「调度中心」,它负责收请求、排优先级、屏蔽干扰、级联扩展。搞懂它,你就搞懂了中断控制器的基本套路。