4. 看门狗超时处理:超时机制、默认动作(复位/中断)、自定义超时回调函数

看门狗的核心价值,说白了就是「超时了怎么办」。

很多初学者以为看门狗超时就是直接复位,其实没那么简单。QNX 给了我们好几层选择,从粗暴的硬件复位,到优雅的中断通知,再到完全自定义的处理逻辑。我个人习惯把这三层叫做「三级响应机制」——你想想看,不同场景下,我们需要的处理方式肯定不一样。

4.1 超时机制:从「喂狗」到「咬人」

看门狗的超时机制,本质上是一个倒计时器。你喂一次狗,计数器就重置到初始值。如果计数器减到0,看门狗就「咬人」了。

在 QNX 中,超时时间是通过 WatchdogCreate()devctl() 设置的。我记得有一次调试一个工业控制器,客户说设备偶尔会死机,但看门狗从来没触发过。后来发现是超时时间设得太长了——30秒,而系统卡死通常只持续5秒就恢复了。嗯,这里要注意:超时时间不是越长越好,也不是越短越好。

我的经验:超时时间一般设为系统最坏情况响应时间的2-3倍。比如你的主循环最长执行100ms,那就设250ms-300ms。留点余量,但别太大。

超时机制有几个关键参数:

  • 初始超时值:喂狗后重置到的计数值
  • 当前计数值:正在递减的计数器
  • 超时动作:计数器到0后执行的操作
  • 喂狗间隔:两次喂狗之间的最大允许时间

这些参数在 QNX 的 sigevent 结构中定义。我刚开始用的时候,总觉得这个结构体字段太多,后来发现其实核心就两个:触发条件和触发后的动作。

4.2 默认动作:复位 vs 中断

QNX 看门狗默认支持两种超时动作:系统复位中断触发。这两种方式各有各的适用场景。

特性 复位模式 中断模式
触发后果 直接硬件复位 触发一个中断信号
恢复方式 系统重新启动 在中断服务程序中处理
适用场景 严重故障、安全关键系统 临时故障、可恢复错误
数据保存 无法保存,直接重启 可以在中断中保存关键数据
调试难度 低,但信息丢失 高,可以记录故障现场

复位模式是最常见的。说白了就是「死透了,重新来」。我在做车载项目时,安全相关的模块必须用复位模式——比如刹车控制器,一旦超时,必须立即复位,不能有任何犹豫。

中断模式则更灵活。超时后先触发一个中断,让你有机会做最后的处理。我曾经在一个数据采集系统中用过这个模式:看门狗超时了,但我不想直接复位,因为还有一批数据没写到Flash里。在中断服务程序里,我把缓存数据紧急写入,然后再手动触发复位。

注意:中断模式不是万能的。如果系统已经严重卡死,中断可能都无法正常执行。这时候复位模式反而是更安全的选择。

4.3 自定义超时回调函数

QNX 最强大的地方在于,你可以完全自定义超时后的行为。通过 sigevent 结构,你可以指定一个回调函数,看门狗超时后自动调用它。

这个回调函数运行在什么上下文中?嗯,这里要讲清楚:它运行在中断上下文。所以你的回调函数必须非常轻量,不能做阻塞操作,不能申请内存,不能调用 printf 这类重型函数。

来看一个实际例子:

#include <sys/siginfo.h>
#include <hw/inout.h>

// 自定义超时回调函数
void my_watchdog_handler(sigevent *event) {
    // 注意:这里运行在中断上下文!
    // 只能做轻量操作
    
    // 记录故障原因到寄存器
    uint32_t *fault_reg = (uint32_t *)0xFFFF0000;
    *fault_reg = 0xDEADBEEF;
    
    // 保存关键上下文
    // 这里不能调用 printf、malloc 等
    
    // 触发系统复位
    // 或者做其他自定义处理
}

int setup_custom_watchdog() {
    int fd;
    sigevent event;
    
    // 打开看门狗设备
    fd = open("/dev/watchdog0", O_RDWR);
    if (fd == -1) {
        return -1;
    }
    
    // 配置自定义事件
    SIGEV_FUNCTION_INIT(&event, my_watchdog_handler, NULL);
    
    // 设置看门狗参数
    devctl(fd, WDIOCSETEVENT, &event, sizeof(event), NULL);
    
    return fd;
}

这段代码里,SIGEV_FUNCTION_INIT 宏是关键。它把我们的回调函数 my_watchdog_handler 注册到了看门狗的事件系统中。超时发生后,QNX 内核会直接调用这个函数。

核心要点:自定义回调函数给了你最大的灵活性。你可以在这里做任何事——记录日志到内存缓冲区、触发其他硬件动作、甚至优雅地关闭外设。但记住:速度要快,操作要轻。

4.4 实际项目中的选择策略

说了这么多,到底该用哪种方式?我根据多年经验,总结了一个简单的决策树:

  1. 系统是否安全关键?(比如汽车、医疗设备)→ 用复位模式
  2. 是否需要保存故障现场?→ 用中断模式或自定义回调
  3. 故障是否可以恢复?→ 可恢复用中断,不可恢复用复位
  4. 调试阶段还是生产阶段?→ 调试用中断,生产用复位

我曾经犯过一个错误:在生产环境中用了中断模式,结果看门狗频繁触发中断,但系统并没有复位,导致故障不断累积,最后整个系统崩溃得更惨。从那以后,我对生产环境的看门狗策略就变得非常保守——能用复位就用复位。

避坑指南:如果你一定要用中断模式或自定义回调,记得在回调函数里设置一个「超时计数器」。连续超时超过3次,就强制复位。这样可以避免「半死不活」的状态。

4.5 总结一下

看门狗超时处理,说白了就是「什么时候咬人,怎么咬」。QNX 给了你三个层次的选择:

  • 复位模式:简单粗暴,适合安全关键系统
  • 中断模式:灵活可控,适合需要故障记录的场合
  • 自定义回调:最大自由度,但需要你小心使用

我个人建议:新手先用复位模式,等你对系统行为足够了解了,再尝试中断和自定义回调。毕竟,看门狗是最后一道防线,别让它成为新的故障源。