2. VxWorks中断架构:中断控制器、中断描述符表、中断处理流程

好,咱们进入第二章。这一章是整个中断管理的骨架,说白了就是搞清楚中断信号从哪来、到哪去、中间经历了什么。我当年刚接触VxWorks时,最头疼的就是这个架构——文档写得晦涩,代码又绕。后来踩了几个坑,才慢慢摸清楚门道。

2.1 中断控制器:硬件层面的“交通警察”

先说说中断控制器。在嵌入式系统里,CPU通常只有一个中断请求引脚(比如IRQ),但外设可能有几十个。谁先响应?谁被屏蔽?这就需要一个仲裁者——中断控制器。

VxWorks支持多种中断控制器,最常见的是x86的8259A PIC、ARM的GIC(Generic Interrupt Controller),还有PowerPC的MPC。我个人习惯把中断控制器理解成一个“交通警察”:

  • 它管理中断源的优先级——哪个设备更紧急,就先放行哪个
  • 它负责中断屏蔽——某些中断暂时不想处理,可以“拦下来”
  • 它提供中断向量号——告诉CPU该去查哪张“表”

举个例子,我在一个ARM Cortex-A9的项目中,用到了GIC-400。这个控制器支持最多256个中断源,每个中断源都可以独立配置优先级和触发方式(电平触发或边沿触发)。配置代码大致是这样的:

/* 初始化GIC中断控制器 */
void gicInit(void)
{
    /* 设置中断优先级阈值 */
    GICD_CTLR = 0x01;           // 使能GIC分发器
    GICC_CTLR = 0x01;           // 使能CPU接口
    
    /* 配置某个外设中断,比如UART */
    GICD_ISENABLER(32) |= (1 << 0);  // 使能中断ID 32
    GICD_IPRIORITYR(32) = 0x80;        // 设置优先级
    GICD_ICFGR(32) = 0x02;             // 边沿触发
}

嗯,这里要注意:不同架构的中断控制器寄存器布局差异很大。你如果从ARM平台移植到PowerPC,千万别直接复制代码——我吃过这个亏,调试了整整两天才发现是寄存器地址不对。

3.2 中断描述符表:VxWorks的“通讯录”

中断控制器把中断信号送给了CPU,CPU接下来要干什么?它得知道这个中断对应哪个处理函数。这就轮到中断描述符表(Interrupt Descriptor Table,IDT)登场了。

在VxWorks里,中断描述符表其实就是一个数组。每个表项(称为中断描述符)记录了:

  • 中断处理函数的入口地址——也就是ISR(Interrupt Service Routine)的地址
  • 中断处理函数的属性——比如是否允许嵌套、是否使用浮点寄存器
  • 关联的设备信息——方便ISR知道是哪个设备触发了中断

VxWorks内部维护了一个全局的中断描述符表,叫intVecTable[]。你可以通过intConnect()函数把中断向量号和ISR绑定起来:

/* 将UART中断连接到ISR */
STATUS intConnect(
    INTR_CONNECT_ENTRY *vector,  /* 中断向量号 */
    VOIDFUNCPTR routine,         /* ISR函数指针 */
    int parameter                /* 传给ISR的参数 */
);

我记得第一次用这个函数时,犯了个低级错误——把参数传成了设备基地址,结果ISR里解引用时直接崩溃。后来才明白,这个参数是int类型,如果你要传指针,得先强制转换一下。

核心要点:中断描述符表是VxWorks中断管理的“中枢神经”。你注册的每个ISR,最终都会映射到这个表里。系统响应中断时,CPU会通过硬件自动查表,找到对应的ISR并执行。

2.3 中断处理流程:从硬件到软件的完整链路

好了,现在我们把中断控制器和中断描述符表串起来,看看一次完整的中断处理流程是怎样的。我画个流程图在脑子里,你跟着走一遍:

  1. 外设触发中断——比如网卡收到一个数据包,拉高了中断引脚
  2. 中断控制器仲裁——检查优先级、是否被屏蔽,然后向CPU发送中断信号
  3. CPU响应中断——保存当前上下文(寄存器、PC等),关中断(防止嵌套),然后查IDT
  4. 执行ISR——VxWorks根据中断向量号找到对应的ISR,开始执行
  5. 中断返回——ISR执行完毕,恢复上下文,开中断,CPU继续执行被中断的任务

这里面有个关键点:ISR执行期间,中断是关闭的。为什么?因为如果ISR还没处理完,又来一个中断,那现场就乱套了。但有些场景下,我们希望高优先级中断能打断低优先级ISR——这就涉及到中断嵌套。

VxWorks默认不支持中断嵌套,但你可以通过配置打开。我曾在某个实时性要求极高的项目中,打开了嵌套支持。结果调试时发现,嵌套深度超过3层后,栈空间就不够用了。嗯,这里要提醒一句:开嵌套前,先算好栈大小

我的经验:ISR里尽量别做复杂操作。我见过有人把printf()写在ISR里,结果系统直接挂掉。ISR的原则是“快进快出”——记录事件、发信号量、唤醒任务,然后立即返回。真正的处理逻辑交给任务去做。

2.4 中断延迟:一个绕不开的话题

说到中断处理流程,就不得不提中断延迟。它指的是从外设触发中断,到CPU开始执行ISR之间的时间。这个时间越短,系统的实时性越好。

中断延迟主要由三部分组成:

延迟来源 说明 典型时间
硬件延迟 中断控制器仲裁、信号传输 几十纳秒到几微秒
CPU响应延迟 保存上下文、关中断、查表 几微秒到十几微秒
软件延迟 ISR入口判断、参数传递 几微秒

我曾经在一个雷达信号处理项目中,要求中断延迟不超过10微秒。当时用的PowerPC处理器,VxWorks 6.9。为了达到这个指标,我做了几件事:

  • 把中断描述符表放在Cache里,减少查表时间
  • 关掉所有不必要的调试打印
  • 把ISR写成纯汇编(虽然维护起来很痛苦)

最后实测延迟在8微秒左右,勉强达标。所以你看,中断延迟优化是个系统工程,硬件和软件都得抠。

避坑指南:我曾经在调试时发现中断延迟突然飙升到100微秒以上。查了半天,原来是某个任务把中断关掉了(intLock())后忘了开。所以,intLock()intUnlock()一定要成对使用,最好用宏封装起来,避免遗漏。

2.5 小结

这一章我们聊了中断架构的三个核心部分:中断控制器负责仲裁和路由,中断描述符表负责映射ISR,中断处理流程则把硬件和软件串了起来。说白了,中断管理就是“硬件触发、软件响应”的协作过程。

下一章,我会带你深入ISR的编写技巧,包括如何避免死锁、如何与任务通信、以及那些年我踩过的坑。咱们下章见。