临界区保护:tx_interrupt_control API详解

临界区保护,说白了就是一段代码在执行时不能被中断打断。你想想看,如果正在更新一个全局变量,突然来了个中断,中断服务程序也去操作同一个变量——那结果肯定乱套。ThreadX 里最直接的临界区保护手段就是 tx_interrupt_control 这个 API。

tx_interrupt_control 的用法

这个函数的作用很简单:开关全局中断。原型长这样:

UINT tx_interrupt_control(UINT new_posture);

参数 new_posture 只有两个取值:

  • TX_INT_DISABLE —— 关中断
  • TX_INT_ENABLE —— 开中断

返回值是调用之前的中断状态。这个返回值很重要,我后面会讲为什么。

核心用法模式:

UINT old_level;
old_level = tx_interrupt_control(TX_INT_DISABLE);
/* 临界区代码 */
tx_interrupt_control(old_level);  /* 恢复原状态 */

注意最后一行,不是直接开中断,而是恢复成原来的状态。为什么?因为调用这个函数的时候,中断可能已经被别的代码关掉了。如果你直接开中断,反而会破坏调用者的预期。我在项目中就见过有人直接写 tx_interrupt_control(TX_INT_ENABLE),结果嵌套调用时出了大问题。

全局中断开关的代价

关中断看起来简单粗暴,但代价不小。我列个表给你看:

代价类型 具体影响 严重程度
实时性损失 所有中断都被阻塞,包括高优先级的中断 高
中断延迟增加 关中断时间越长,中断响应越慢 高
嵌套问题 嵌套关中断容易导致状态混乱 中
调试困难 关中断导致的 bug 很难复现 高

我曾经在一个电机控制项目里,关中断时间超过了 100 微秒,结果编码器中断丢了脉冲,位置计算直接偏了。从那以后,我对关中断的时长就特别敏感。

替代方案:更细粒度的保护

全局开关太粗暴了,有没有更优雅的办法?当然有。ThreadX 提供了几种替代方案:

1. 使用信号量保护共享资源

信号量只阻塞等待该资源的任务,不影响中断响应。适合任务间共享数据的场景。

2. 使用互斥量 + 优先级继承

互斥量比信号量多了优先级继承机制,能避免优先级反转。我建议在任务间保护复杂数据结构时优先用这个。

3. 使用中断锁(特定中断屏蔽)

很多 MCU 支持单独屏蔽某个中断源,而不是关全局中断。比如只关定时器中断,其他中断照常响应。

我的经验:能用信号量或互斥量解决的问题,就别关全局中断。关中断只适合那种「就几条指令,绝对不能被打断」的场景,比如操作链表头指针、更新 FIFO 读写索引。

什么时候必须用 tx_interrupt_control?

有些场景,替代方案确实不好使:

  • 中断服务程序内部访问共享资源 —— 信号量在 ISR 里不能用,只能用关中断
  • 操作硬件寄存器序列 —— 比如配置 DMA 描述符链,中间不能被打断
  • 实现原子操作 —— 比如「检查-修改-写回」这种 read-modify-write 序列

避坑指南:我曾经在 ISR 里调用了一个函数,那个函数内部又调用了 tx_interrupt_control(TX_INT_DISABLE)。结果中断嵌套关了两层,恢复时只恢复了一次,中断就一直关着。系统直接死掉。所以记住:关中断和开中断必须成对出现,且要保存和恢复旧状态。

性能对比:关中断 vs 替代方案

我整理了一个对比表,方便你选型:

保护方式 中断延迟影响 代码复杂度 适用场景
tx_interrupt_control 所有中断被阻塞 低 ISR 内、极短临界区
信号量 无影响 中 任务间共享数据
互斥量 无影响 中 需要优先级继承的场景
特定中断屏蔽 仅阻塞特定中断 高 对特定外设有实时要求

核心逻辑流程图

下面这张图展示了临界区保护的核心决策逻辑:

临界区保护决策流程 需要保护临界区 是否在 ISR 中执行? (中断服务程序) 是 否 tx_interrupt_control 关全局中断 临界区是否极短? (几条指令) 是 否 tx_interrupt_control 关全局中断 信号量 / 互斥量 任务级保护 临界区执行完成 说明:ISR 内只能使用 tx_interrupt_control;任务级短临界区也可用它,长临界区优先用信号量/互斥量

实际项目中的建议

嗯,这里要注意几点:

  • 关中断时间控制在 10 微秒以内 —— 超过这个值,高优先级中断就可能丢事件
  • 不要在关中断期间调用任何可能阻塞的函数 —— 比如获取信号量、延时,那会直接死锁
  • 用 tx_interrupt_control 保护共享变量时,记得加 volatile —— 否则编译器优化可能让你怀疑人生

我的习惯:每次写完关中断的代码,我都会在注释里写明「为什么这里必须关中断,不能用信号量替代」。这样三个月后回来看代码,还能想起当时的决策依据。

说白了,tx_interrupt_control 是一把双刃剑。用好了能精准保护临界区,用不好就是系统崩溃的根源。我个人建议:能用替代方案就用替代方案,实在不行再上全局中断开关,而且一定要把关中断时间压到最短。