异常处理框架:异常处理钩子函数(excHookup)、异常处理任务的创建、异常处理优先级

异常处理框架,说白了就是系统在遇到异常时,该找谁、怎么找、谁先处理的一套规则。我这些年调试VxWorks系统,遇到最多的问题就是异常处理没搭好,导致系统挂了连个日志都捞不到。今天咱们就把这块彻底讲透。

异常处理钩子函数(excHookup)

先说说excHookup这个函数。它的作用很简单——注册一个回调函数,当异常发生时,系统会先调用你注册的这个函数。你可以把它理解成「异常报警器」。

核心函数原型:

STATUS excHookup
    (
    int vector,          /* 异常向量号 */
    VOIDFUNCPTR routine, /* 回调函数指针 */
    int arg              /* 传递给回调的参数 */
    );

我个人习惯在系统初始化阶段就把所有关键异常都挂上钩子。比如总线错误、非法指令、浮点异常这些,一个都不能漏。

举个例子,我曾经在一个雷达信号处理项目中,系统偶尔会莫名其妙重启。排查了三天,最后发现是浮点运算溢出导致的。当时要是提前挂了浮点异常的钩子,半天就能定位问题。

我的经验:钩子函数里不要做复杂操作。打印日志、保存现场、触发看门狗就够了。千万别在钩子里申请内存、操作文件系统——异常上下文里这些操作很可能再次触发异常,形成死循环。

异常处理任务的创建

钩子函数只是第一步。真正处理异常逻辑,需要单独创建一个任务。为什么?因为钩子函数运行在异常上下文中,栈空间有限,而且不能阻塞。

我建议的做法是:钩子函数只负责「通知」,真正的处理逻辑交给一个高优先级的异常处理任务。

/* 异常处理任务示例 */
void excHandlerTask(void)
{
    int excCode;
    
    while (1)
    {
        /* 等待异常通知 */
        msgQReceive(excMsgQId, (char *)&excCode, sizeof(excCode), WAIT_FOREVER);
        
        /* 根据异常类型做处理 */
        switch (excCode)
        {
        case EXC_BUS_ERROR:
            /* 总线错误:记录地址、尝试恢复 */
            logMsg("Bus error detected, attempting recovery...\n", 0,0,0,0,0);
            break;
            
        case EXC_ILLEGAL_INSTRUCTION:
            /* 非法指令:可能是代码损坏 */
            logMsg("Illegal instruction, task may be corrupted\n", 0,0,0,0,0);
            taskSuspend(taskIdSelf());
            break;
            
        default:
            logMsg("Unknown exception: 0x%x\n", excCode,0,0,0,0,0);
            break;
        }
    }
}

注意:异常处理任务的栈空间要预留充足。我见过有人只给了2048字节,结果处理过程中栈溢出,系统直接挂了。建议至少4096字节起步。

异常处理优先级

优先级这块,很多人会犯一个错误——把异常处理任务设成最高优先级。你想想看,如果异常处理任务优先级最高,那它运行时其他任务都抢不过它。万一处理逻辑里有bug,整个系统就卡死了。

我个人的经验是:异常处理任务的优先级比普通应用任务高,但比中断服务程序低。具体数值要看你的系统负载情况。

优先级范围 典型用途 我的建议
0-50 中断服务、时间关键任务 异常处理任务不要放这里
51-100 异常处理、关键监控任务 推荐放在80-90之间
101-200 普通应用任务 异常处理任务优先级要高于它们
201-255 后台任务、空闲任务 异常处理任务不要放这里

为什么会这样?因为异常处理任务需要及时响应,但不能抢占中断服务。我曾经在一个项目中,把异常处理任务优先级设成了10,结果它和中断服务抢CPU,导致定时器中断丢失,系统时间都乱了。

避坑指南:我曾经犯过一个低级错误——多个异常共用一个处理任务。结果总线错误和非法指令同时发生时,处理任务只能处理一个,另一个被忽略了。正确的做法是:每个关键异常单独挂钩子,或者用消息队列把异常信息排队处理。

嗯,这里还要提一句。异常处理任务的优先级不是一成不变的。系统启动阶段、正常运行阶段、降级运行阶段,优先级可以动态调整。我习惯在系统初始化时把异常处理任务优先级设高一些,等系统稳定后再降下来。

说白了,异常处理框架就像你家的消防系统。钩子函数是烟雾报警器,异常处理任务是消防队,优先级就是消防队的出警顺序。三者配合好了,系统才能扛得住各种意外。