引言

在VxWorks下,使用系统本身提供的接口函数ExcConnect()来进行异常处理只对PowerPC系列处理器有效,对目前使用同样广泛的Intel 80x86系列处理器和ARM系列处理器无法提供有效支持。本文主要针对目前VxWorks异常机制可移植性差的问题,提出一种及时准确并且与具体处理器类型无关的捕捉异常的方法。

1 VxWorks 下异常分析与处理

在VxWorks下如果使用默认的异常处理机制,处理结束后:产生异常的任务将被悬挂,且该消息将被传送到控制台。可以看出,如果不对异常进行处理,使用默认的异常处理方法,对于一个系统来说往往是致命的。所以我们必须对系统默认的异常处理方法进行修改和完善。

可以将异常处理流程分为以下5个阶段:

  1. 一个软件错误发生
  2. 错误的原因和性质被一个异常对象携带
  3. 程序检测这个异常对象,或者允许它的存在,或者由其主动上报
  4. 检测代码决定如何处理异常
  5. 异常处理完毕,恢复程序并继续执行

2 跨平台异常处理模块的实现

2.1 整体实现方案

本文设计的异常处理模块采用钩子函数的方式接收操作系统传入的异常现场;在内存的保留区保存传入参数,并释放信号量以激活守护任务;守护任务保存未解析的现场信息;采用堆栈回溯和匹配函数序言的方式进行函数调用链分析,将结果保存到内存保留区;若存在外部存储介质,则将解析后的现场和函数调用链分析结果写入文件;对应用层提供钩子函数以实现恢复策略支持。

异常处理模块的整个流程主要由两部分组成:异常处理初始化流程和异常钩子函数流程。初始化流程主要包括初始化全局变量和挂接异常处理钩子函数。异常处理的处理过程设计如图所示:

VxWorks Exception Process

2.2 “堆栈分析”

异常处理过程中的关键是“堆栈分析”,即使用堆栈回溯的方法实现现场分析,该功能的实现也是异常处理的难点。现场分析与处理器类型密切相关,需针对不同系列处理器分别分析异常类型、现场信息、运行栈结构、函数的序言和尾声等内容。下面以x86 处理器为例进行说明。

2.2.1 运行时堆栈结构

Intel80x86手册中把指令执行时由CPU控制器产生的同步中断称为异常。异常是由程序的错误产生,或者由操作系统内核必须处理的异常条件所产生。通常,编译器产生的代码中对堆栈的结构和使用存在一个约定,其中一个重要的概念是堆栈帧(Stack Frame)或称为活动记录(Active Record)。一个堆栈帧就是运行栈中的一个存储块,它按照一定的规则描述了当前函数的调用信息,一个栈框架对应一次函数调用。Intel80x86的运行栈结构如图2所示:

X86 Stack

2.2.2 函数序言与尾声的匹配

代码段中,特征十分明显的函数序言恰好位于函数入口处,所以只要在机器码代码段找到函数序言就获得了其入口地址。对一个帧的分析需要一个搜索指针,初始值为现场PC或被调函数帧的返回地址ret[i],指针沿代码段向后(低地址)移动,直到匹配函数序言的第一条语句为止。这时该指针的值即为该函数的入口地址。因此,匹配到函数序言,就找到了函数的入口地址。函数的入口地址得到以后,通过VxWorks提供的函数symFindByValue()获得函数名。前提是生成的系统映像中包括了系统符号表。

序言匹配的方法是:选择一个位于代码段中待分析的函数体内起点指针,向低地址方向移动该指针,直至找到函数序言为止。

2.3 异常抛出机制

异常抛出机制指的是:当用户管理的数据结构出现严重错误时或用户定义的全局变量失去一致时,用户进程主动触发一个事先给定的异常,然后由自定义的异常处理任务对该异常进行处理,并确定这个问题发生的进程位置。这种方案实现简单,抛出的异常和硬件定义的异常采用同一套处理流程。异常抛出接口定义为:

void OSS_ThrowOutExc
( WORD wEosExcNo, 
  WORD32 dwFunc
) 

其中,参数wEosExcNo 为支撑统一定义的异常类型;dwFunc为抛出异常的处理函数。

3 结论

本文采用钩子函数的方式接收操作系统传入的异常现场,避免了对寄存器进行直接操作,从而实现了跨平台获取异常现场信息,使软件异常处理的可移植性和健壮性大大增强。这种异常处理的设计具有很高的推广和实用价值,目前已应用在航行数据记录仪、列车运行监控记录装置的实时数据库中。