《电子技术应用》

嵌入式操作系统异常处理框架设计与实现

2017年电子技术应用第5期 作者:王继刚1,方 芳2,张华强1
2017/6/12 13:34:00

王继刚1,方  芳2,张华强1

(1.中兴通讯股份有限公司 技术规划部,江苏 南京210012;

2.信息产业电子第十一设计研究院科技工程股份有限公司 信息中心,四川 成都610021)


    摘  要: 异常处理对于提升嵌入式系统可靠性至关重要,而传统面向硬件的异常处理方法难以适应当前嵌入式应用的发展。通过分析嵌入式系统新的异常处理需求,提出一种结构化异常处理框架,在传统的异常处理机制基础上引入了新的功能特性,并以电信级嵌入式Linux为原型,描述了该框架的具体实现及应用。与其他主流异常处理机制的对比结果表明,异常处理框架功能完善,接口丰富,多级异常处理流程为系统解决各类异常提供多层面的支持。

    关键词: 嵌入式系统;异常处理;状态机;结构化模型

    中图分类号: TP302

    文献标识码: A

    DOI:10.16157/j.issn.0258-7998.2017.05.014


    中文引用格式: 王继刚,方芳,张华强. 嵌入式操作系统异常处理框架设计与实现[J].电子技术应用,2017,43(5):60-63,66.

    英文引用格式: Wang Jigang,Fang Fang,Zhang Huaqiang. Design and implementation of exception handling framework for embe-dded operating system[J].Application of Electronic Technique,2017,43(5):60-63,66.

0 引言

    随着后PC时代的到来,嵌入式系统已广泛应用于航空航天、通信、国防等可靠性要求较高的关键领域,其复杂度及功能性日益增强。同时,为了高效使用资源,多应用、多任务并行的软件设计方法被普遍采用,这也不可避免地导致了系统中各类错误和异常增多。当异常出现时,如果不能有效处理,很有可能导致应用程序终止,系统崩溃,甚至引发灾难性事故,这是人们不愿看到的。然而,传统面向硬件的异常处理方法无法适应当前嵌入式软件的变化,必须引入新的机制,保证系统可靠稳定运行。

    通过深入分析嵌入式系统对异常处理的需求,参考现有异常处理机制的设计思想,本文提出一种结构化的异常处理框架模型,将一些新的功能引入其中,并基于自主研发的电信级嵌入式操作系统(Carrier Grade Embedded Linux,CGEL)[1],具体实现了该异常处理框架。

1 嵌入式系统异常处理需求

    传统的嵌入式系统异常处理通常采用中断响应方式,当异常发生时,系统通过异常向量表跳转到对应的异常处理函数中。随着嵌入式系统及应用日益复杂,异常的种类也在不断增多,出现了许多新的需求。本文通过分析嵌入式应用软件特征,归纳出以下异常处理新的需求。

    (1)异常处理框架。为了满足日益复杂的异常处理场景,提供具有基本异常处理流程及应用开发接口的框架是必要的。该框架既可使异常从发生到处理都具备完整的处理流程,同时,也为应用开发者提供标准的接口,用于自定义异常处理函数。开发者自定义的内容不会改变整个异常处理的流程,但能够丰富某个阶段的异常处理功能。

    (2)状态机。缺少异常处理会降低系统的可靠性,而有缺陷的异常处理也会导致严重的系统故障。研究表明[2],有相当一部分错误是由不正确的异常处理造成的。状态机可以有效避免异常处理过程中出现二次异常的问题,通过定义异常处理过程中的各个状态及状态间的跳转条件,规定了各状态下可能的动作,从而识别异常处理过程中的二次异常,并予以合理地解决。

    (3)嵌套异常处理。现代嵌入式应用往往包含多个任务,存在各种嵌套关系。异常处理不仅需要随着函数调用的深入形成嵌套关系,还要随着函数调用的返回逐层解开,并回退嵌套的堆栈[3]。当异常发生时,异常处理框架首先对匹配异常处理函数进行搜索,若异常发生在第10层的函数调用,而异常处理函数在第5层,异常处理框架则需要将当前堆栈从第10层逐层回退到第5层,恢复第5层的运行环境进行异常处理。

    (4)自定义钩子函数。传统的异常处理机制与处理器架构紧密相关,需要通过汇编语言对寄存器操作,这导致软件的移植性和健壮性很差。现代操作系统提供了钩子挂接机制,在应用程序中通过钩子函数接入自定义的异常处理模块,当异常发生时,钩子函数会捕捉到内核发出的异常信号,获得控制权并予以处理。通过钩子函数能够避免对寄存器直接操作,获取异常信息与处理器无关,有效增强嵌入式软件的移植性和健壮性。

    (5)异常抛出机制。传统的嵌入式软件异常处理只能响应系统级异常,无法处理程序逻辑异常。而随着系统复杂性的增加,程序逻辑上错误也会对系统造成严重的影响。异常抛出[4]是处理程序逻辑异常的有效机制,但目前大多数嵌入式软件编程语言并没有提供这种机制,比如C、汇编等。所以异常处理框架需提供异常抛出机制,开发者通过调用相关函数启动异常抛出功能,对代码段进行监控,捕捉异常信息并处理。

    面对现代嵌入式系统对异常处理机制提出的需求,国内外研究者进行了深入研究并取得了一系列成果。文献[5]采用有限状态机,通过对异常上下文信息进行分析,有效提升了异常处理的效率和准确性。但异常处理策略单一,并没有解决异常处理过程中二次异常和异常扩散等问题。文献[6]针对嵌入式系统提出了一种异常定位方法,对系统运行时出现的异常进行分类,但并未给出异常处理的策略。文献[7]提出了一种分层次多策略的异常处理框架,通过检查点回滚恢复实现多级异常处理。然而,该框架无法判别异常处理方法本身的缺陷。

    针对这些新的需求,本文设计了一个基于结构化模型的异常处理框架,可提供多层面的解决方案,涵盖嵌入式系统异常处理的绝大多数特性,为系统处理各类异常提供有效支持。同时,异常处理框架还具有丰富的钩子函数挂载接口,开发者可挂载自定义的异常处理模块,提高程序运行的健壮性。

2 异常处理框架的设计实现

2.1 异常处理框架模型

    异常处理框架定义了对异常的捕获、处理、保存以及跟踪等一整套流程。从功能上可分为3个模块:异常控制、异常捕获及异常处理。其中,异常控制是一个逻辑概念,它负责维护异常处理状态机,控制着整个处理流程,防止异常处理过程中出现二次异常;异常捕获则完成系统及应用在运行时异常的捕获,并通过直接调用或发送信号等手段启动异常处理模块;异常处理模块接收异常信息,对捕获到的异常提供基本处理方法,也支持用户自定义的处理手段。

    图1是异常处理框架在CGEL中的具体实现模型,框架基于标准的信号机制实现。其中异常捕获对系统及应用在运行过程中出现的异常进行捕获,通过信号调用异常处理模块予以处理,整个处理流程由异常控制模块维护。同时,异常处理框架还可以调用状态监控等系统服务,进行错误信息的存储和告警操作,对异常进行有效定位。

qrs1-t1.gif

    为了避免应用程序对底层硬件直接操作,异常处理框架为应用开发提供了线程级和进程级两个层次的回调钩子函数。应用中每个线程都分配一个异常回调函数链表,当出现异常时,框架会从该线程异常回调链表尾部开始搜索匹配的异常回调函数,链表还可支持程序中多层异常嵌套。而进程级异常回调函数对整个应用有效,该函数只能被挂接一次,当所有线程都无法处理异常时,进程级异常回调函数将被调用。开发者可以根据需要在不同层次的回调钩子上挂接自定义的异常处理程序。

2.2 异常处理状态机

    通过对异常处理所处状态的统一管理,状态机能够帮助异常处理过程有序地进行,支持各种异常处理方法的协调运行。当系统出现异常,状态机会了解出现异常的状态,比如是否进入了循环异常、是否为系统异常等,并决定处理异常的动作,指导异常处理框架的工作,用最优的方式解决异常。为了避免异常重复进入,异常处理模块的状态机设计了3种状态:正常态-Normal、异常处理态-ExcProc、异常升级处理态-ExcEscal。各状态含义及跳转条件如表1所示。

qrs1-b1.gif

    图2是异常处理状态机的迁移图。在CGEL中,应用状态在线程的线程本地存储(Thread Local Storage,TLS)中保存,如果使用pthread库时,可使用库提供的TLS功能,否则需要实现一个简单的TLS功能来保存每个线程的异常处理状态。

qrs1-t2.gif

2.3 多级异常处理流程

    异常控制状态机划分了异常处理过程的不同状态,如何利用其进行异常处理,则是异常处理流程的工作。异常处理框架提供了一个开放式的多级处理流程,开发者可根据应用情况挂接不同级别的自定义异常处理函数,逐级完成异常处理功能。异常处理模块程序流程图如图3所示。下面介绍使用框架进行多级异常处理的步骤:

qrs1-t3.gif

    步骤1:在需要使用异常处理功能的应用程序中,对异常处理框架进行初始化,挂接主处理函数Usr_ExcMain();

    步骤2:函数Usr_TrdExcHdlReg()和Usr_TrdExcHdlUnReg()用来注册/注销线程级异常处理钩子,每个线程维护一个异常处理钩子函数链表,新注册的线程级异常处理钩子以节点形式被挂接在链表尾;

    步骤3:函数Usr_ProcExcHookReg()和Usr_ProcExc-HookUnReg()用来注册/注销进程级异常处理钩子,进程级异常处理钩子对整个应用进程有效,只能被注册一次,后面注册的钩子将覆盖前面注册的;

    步骤4:当有线程出现异常,框架将调用Usr_ExcMain()进入主处理流程,从尾部节点开始遍历该线程的异常处理函数链表,通过节点上有效异常过滤函数,判断节点能否处理该异常。若能则调用该节点上挂接的异常处理钩子,否则获取前一节点;

    步骤5:如遍历完线程异常处理钩子链表仍无法修复,异常处理框架将把该异常升级为进程全局异常,获取进程级异常处理钩子函数gpf_UsrExcHook()予以处理;

    步骤6:若所有的异常处理都失败,将根据用户在函数Usr_SetExcExitAct()中设定的动作退出该应用进程。

    上述步骤实现细节被封装在异常处理框架中,用户只需调用框架所提供的注册接口挂接自定义的异常处理函数,从而提高了异常处理机制的可移植性和健壮性。

2.4 异常捕获

    异常捕获模块主要完成对各类异常的动态捕捉,对于捕获到的硬件异常,异常处理框架通过传统的中断响应方式处理。对于不同的处理器体系,具体的异常编号和种类会有所不同,但主要有:(1)CPU运行异常,如浮点错误、除零错误、越权保护错误、非法指令等;(2)内存管理异常,如数据写异常、页面异常、缓冲区异常等。

    除了以上这些硬件异常,应用程序还有可能出现逻辑异常。这就需要异常处理框架能够对代码段进行监视,并可启动异常处理流程。抛出异常是一种处理程序逻辑异常的有效机制,其代码如下:

    TRY_BEGIN

        //需要保护的代码

    THROW 异常

    TRY_END

    CATCH_BEGIN

        //异常处理代码

    CATCH_END

    通过try-catch-throw语句,可以抛出程序中的异常,转由正常控制流以外的代码处理。抛出异常对于分离异常和正常代码,有效增强异常处理功能非常重要。由于C语言缺乏对抛出异常的支持,需要在异常处理框架中添加类似功能。同时,针对C语言特征对抛出异常机制进行了修改,例如:C语言没有类的概念,框架弱化了异常类型的概念,用异常号(unsigned int)表示异常类型;C语言无关键字try、catch、throw的支持,框架借助宏定义予以实现,如表2所示。

qrs1-b2.gif

    除了设计必要的宏外,为了支持在多线程环境实现嵌套的try块,异常处理框架还定义了一种处理try块的栈式结构,该栈式结构在线程初始化阶段动态申请空间,其指针保存在TLS中,在线程终止时释放。

3 相关工作

    一些主流的嵌入式操作系统也提供了异常处理机制,下面将分析这些操作系统中异常处理机制的特点,并与本文提出的异常处理框架进行对比。

    从异常处理机制完备性和功能性角度而言,Windows CE[8]是比较全面的,它具有结构化的异常处理机制,为开发者提供了有力的处理程序错误或异常的武器。但针对C语言的异常抛出功能,Windows CE本身不支持,只能结合编译器提供语言级的支持。另外,其异常处理流程没有状态机控制,无法解决在异常处理过程中出现的错误。

    VxWorks[9]是业界非常著名的嵌入式实时操作系统,它提供的全局异常钩子回调函数在嵌入式系统中实用性很强,同时,VxWorks还提供了操作系统缺省异常处理,会对异常发生现场作函数调用链分析,并将异常信息通过终端输出。但是,VxWorks的异常处理机制非常简单,不支持异常处理状态机以及异常抛出等高级功能,难以满足现代嵌入式软件的需求。

    由于具有丰富的系统功能、高度定制性及开放源码特征,Linux在嵌入式领域得到了越来越广泛的关注。Linux内核[10]没有提供统一的异常处理框架,异常处理流程被归类在与CPU体系相关的代码中,虽然流程基本一致,但随着CPU体系不同还是有所差异。应用程序需要采用接受异常信号的方式调用异常处理流程,不支持钩子回调函数,不支持异常处理状态机,这些都降低了应用程序异常处理的健壮性及可移植性。

    本文的异常处理框架结合了主流嵌入式系统异常处理机制的特点,具有完备的动态链表模型和状态机,可以有效处理二次异常,还提供适合于嵌入式系统使用的回调函数,同时对程序可能出现的逻辑异常提供了异常抛出机制,满足现代嵌入式软件对异常处理的需求。表3总结了上述各异常处理机制的特点,并与CGEL进行了功能对比。

qrs1-b3.gif

4 结束语

    异常处理已成为提升嵌入式系统健壮性和可用性的关键,本文针对现代嵌入式应用软件新的异常处理需求,提出了一种结构化的异常处理框架模型,框架包含了3个主要功能模块:异常捕获、异常处理、异常控制,涵盖嵌入式系统异常处理绝大多数特性,多级异常处理流程也为系统处理各类异常提供了多层面支持。

    目前,异常处理框架已在电信级嵌入式操作系统CGEL上实现,并成功应用于多款电信设备上,大大增强了系统的可靠性。同时,良好的编程接口缩短了应用软件的开发周期,提高了开发质量。高性能和分布式是未来嵌入式系统的主要特征,动态改变异常处理程序优先级、分布式的异常抛出及处理都是重要的研究方向,有待进一步研究。

参考文献

[1] 王继刚,郑纬民,钟卫东,等.基于Linux的混合实时操作系统[J].清华大学学报,2009,49(7):1012-1015.

[2] Herbert Hecht.A systems engineering approach to exception handling[C].Proceeding of the Third International Conference on Systems.Washington DC,USA,2008:190-195.

[3] 朱剑锋,缪万胜,康介祥.基于堆栈回溯的异常处理[J].计算机工程与设计,2014,35(12):4176-4180.

[4] Mao Chengying,Lu Yansheng.Study on the analysis and testing of exception handling in C++ programs[J].Mini-Micro Systems,2006,27(3):481-485.

[5] Cabdq Filho,Rmc Andrade,Ls Rocha,et al.ConExT-U:A context-aware exception handling mechanism for task-based ubiquitous systems[C].28th International Conference on Advanced Information Networking and Applications Work-shops.BC,Canada,2014:127-132.

[6] SAWADPONG P,ALLEN E B,WILLIAMS B J.Exception handling defects:an empirical study[C].14th International Symposium on High-Assurance Systems Engineering.Washington DC,USA,2012:90-97.

[7] Lu Zhou,Zhang Kailong,Zhou Xingshe.A software Fault-Tol-erant method based on exception handing in RT/E system[C].10th IEEE International Conference on Trust,Security and Privacy in Computing and Communications,Changsha,China,2011:1283-1287.

[8] 羊建林,周安民.Windows异常处理与软件安全[J].信息安全与通信保密,2011,9(4):58-60.

[9] Wind River.VxWorks kernel programmers guide 6.9[M].California:Wind River Systems,2011.

[10] Robert Love.Linux kernel development[M].USA:Addison-Wesley Professional,2010.

继续阅读>>