第4章:中断处理程序(ITH) — 真正的中断执行者
好,咱们接着聊中断。上一章讲了ISR,那个轻量级的“哨兵”。这一章的主角是ITH——Interrupt Thread,中断处理程序。说白了,ISR负责“接电话”,ITH才是真正“干活”的人。
我个人习惯把ITH叫做“中断线程”。为什么要有线程?因为ISR里不能做太多事,否则系统就卡死了。你想想看,如果每个中断都在内核态里跑个几十毫秒,那实时性从何谈起?
4.1 ITH工作机制
ITH本质上是一个QNX线程。但它不是普通的线程,它和中断源绑定在一起。当ISR被触发后,它会做两件事:
- 快速处理硬件寄存器 — 比如清中断标志、读数据到缓冲区
- 通知ITH线程 — 通过信号量或消息,告诉ITH“该你上场了”
ITH线程随后被调度执行。它运行在用户空间,拥有完整的线程上下文。这意味着它可以调用任何POSIX API,可以阻塞,可以等待资源。ISR里不能做的事,ITH全都能做。
核心区别一句话总结:
ISR是原子操作,不可阻塞,运行在内核态。
ITH是普通线程,可以阻塞,运行在用户态。
我在项目中遇到过一种情况:某设备的中断频率很高,ISR里只做了“读FIFO数据到内存”这一步,然后立即唤醒ITH。ITH负责协议解析和数据处理。这样ISR的执行时间被压缩到微秒级,系统响应非常稳定。
4.2 ITH与ISR的协作
ISR和ITH怎么配合?我画个简单的流程:
硬件中断发生
↓
CPU进入中断上下文
↓
ISR被调用(内核态)
├─ 读取硬件状态
├─ 清除中断标志
├─ 拷贝关键数据
└─ 发送信号/消息给ITH
↓
ISR返回
↓
调度器决定何时运行ITH
↓
ITH执行(用户态)
├─ 处理数据
├─ 调用驱动API
├─ 可能阻塞等待资源
└─ 通知应用层
这里有个关键点:ISR和ITH之间如何传递数据?
我建议使用pulse(脉冲消息)或者信号量。脉冲消息是QNX特有的轻量级IPC机制,开销极小。ISR里发送一个脉冲,ITH里接收并处理。代码大概长这样:
// ISR部分
const struct sigevent *isr_handler(void *arg) {
// 读取硬件寄存器
uint32_t status = in32(REG_STATUS);
// 清除中断
out32(REG_CLEAR, 1);
// 发送脉冲给ITH
MsgSendPulse(ith_coid, -1, SIGEV_PULSE_NOCODE, status);
return NULL; // 不重新调度
}
// ITH部分
void *ith_thread(void *arg) {
int rcvid;
uint16_t code;
uint8_t data[256];
while (1) {
// 等待脉冲
rcvid = MsgReceivePulse(chid, &data, sizeof(data), NULL);
if (rcvid == 0) {
// 收到脉冲,开始处理
process_data(data);
}
}
}
经验之谈:
ISR里尽量只做“必须在内核态做的事”。比如读一个寄存器、清一个标志。其他所有事情都丢给ITH。我曾经见过一个项目,ISR里做了内存分配——结果系统偶尔崩溃,查了三天才发现是ISR里调用了malloc。记住:ISR里不要动态分配内存!
4.3 ITH调度策略
ITH既然是线程,那它就有优先级。QNX的调度策略对ITH同样适用。但这里有个坑:ITH的优先级应该设多高?
我一般遵循这个原则:
- 硬实时中断(比如电机控制、传感器采样):ITH优先级设为最高,通常63或64
- 软实时中断(比如网络包处理、UI事件):ITH优先级中等,30-40左右
- 非实时中断(比如日志、统计):ITH优先级低,10-20
为什么不能所有ITH都设最高优先级?你想想看,如果两个高优先级ITH同时就绪,调度器会先运行一个,另一个被阻塞。如果那个被阻塞的ITH持有某个资源,高优先级的ITH反而可能被低优先级的线程阻塞——这就是优先级反转。
注意:
QNX虽然支持优先级继承协议,能缓解优先级反转问题,但最好的办法是合理规划优先级。不要把所有ITH都堆在最高优先级上。
ITH的调度策略我推荐使用SCHED_FIFO(先进先出)或SCHED_RR(时间片轮转)。对于中断处理线程,SCHED_FIFO更常见,因为中断处理通常需要尽快完成,不需要被时间片打断。
设置优先级和策略的代码:
struct sched_param param;
param.sched_priority = 50; // 中等优先级
pthread_setschedparam(ith_thread_id, SCHED_FIFO, ¶m);
我曾经在一个多核系统上调试中断响应。有个ITH优先级设得太低,结果被其他线程抢占了CPU,导致中断响应延迟从5微秒飙升到200微秒。后来我把这个ITH绑定到某个专用核上,并设置高优先级,问题就解决了。
4.4 避坑指南
最后分享几个我踩过的坑:
- ITH里不要做死循环等待 — 除非你明确知道自己在做什么。否则一旦ITH卡住,整个中断处理链路就断了。
- 注意ITH的栈大小 — 默认栈可能不够用。如果ITH里调用了复杂的库函数,记得增大栈空间。
- ISR和ITH之间的数据同步 — 用原子操作或锁。但锁不能在ISR里用!只能用原子变量。
- 测试中断频率上限 — 每个系统都有极限。我习惯用
cyclictest或自己写一个中断注入工具,压测一下ITH的吞吐能力。
总结一下:
ITH是QNX中断处理的核心。它把中断处理从内核态搬到用户态,既保证了实时性,又提供了灵活性。ISR负责“快”,ITH负责“全”。两者配合好了,你的系统才能既稳定又高效。
嗯,这一章就到这里。下一章咱们聊聊中断的嵌套和优先级管理,那又是一个容易出问题的地方。