VxWorks任务编程异常处理
扫描二维码
随时随地手机看文章
在任务运行过程中,会出现一些异常的情况,导致任务不能正常运行或者对操作系统造成影响。一般来说,这些异常是由程序的逻辑错误造成的,防止这些异常情况的出现和出现后进行补救就有格外重要的意义。
CPU异常
在VxWorks中,当任务的指令执行中出现了指令非法、地址寻址错误、总线错、除数为0等情况时,就会出现CPU异常。比较常见的情况是,指针地址非法或者数组下标越界就有可能存取有效地址空间以外的地址而造成CPU异常。VxWorks提供一个异常处理句柄(handler)和一个名为tExcTask的任务来处理异常。异常出现后任务成为挂起状态(suspend),并且不能转变为其它状态。在VxWorks中,有一个异常向量表来对应各种异常,外部中断也作为一种特殊的异常。VxWorks的做法是把多种异常的处理映射到同一个异常处理函数进行处理,并且VxWorks提供了向这个异常处理函数中钩挂用户的异常处理函数的接口excHookAdd(),也可以将某一个异常向量映射到指定的处理函数。
代码重入与共享
在应用中,可能会出现多个任务调用同一段代码的情况,由于任务占用CPU是串行的,不会出码资源使用冲突。但是,不同优先级的任务同时调用同一段代码,则可能出现低优先级任务执行某一函数时被执行该函数的高优先级任务打断的情况,如果函数中要改写全局变量而没有使用互斥,就有可能导致错误的存取。例如在中断中调用内存分配或者释放函数,如果某个任务正在调用内存分配函数或者是内存释放函数,打断该任务时会造成异常,可能导致内存泄漏,甚至有可能会因在中断中异常而reboot。另外,如果多个任务共用的代码中有全局变量且使用目的不同,或者多个任务的代码中有全局变量同名的情况,则有可能造成变量使用中的错误。VxWorks提供了任务变量(taskVar)的方法来解决这个问题,任务可以将使用的全局变量作为任务变量独立使用,添加的任务变量保存在任务的上下文中,任务切换时保存当前内容。
符号表的使用
VxWorks中有模块()的概念。装载模块完成目标代码文件在内存中的链接,并可以将目标代码文件中的函数与全局变量加入符号表。符号表中的符号对C语言编写的函数以原来名字命名,对于C++语言的函数则是在后面加上形参的数据类型作为符号名。如f1( )的符号名为f1__Fv,最后的v表示void类型;f2(int)符号名为f2__Fi,f3(int,int)为f3__Fii,依此类推。代码的编译过程中并不对要使用的函数和变量进行检查。例如调用一个并不存在的函数编译并不报错,编译器认为此函数可能在操作系统内核中或者已经下载的目标文件中,但在目标文件下载时会找不到要调用的函数。如果符号表中的符号出现了重名,譬如两次下载的目标文件中有函数重名,则要作散列处理,之后对该函数的调用是最后加入符号表的函数,而之前已经装载的模块则不会受到影响。如果应用程序中使用了与操作系统内核同名的符号,则对操作系统某些API函数的调用将会失败。
特殊的任务保护
在VxWorks中,当一个任务被删除,其它任务不会得到通知,而且由于任务间的独立性,每一个任务可以无限制地删除其它任务。在应用中,我们可能会把需要保护任务误删除。VxWorks 提供的两个函数taskSafe( )和taskUn( )将通知意外删除任务而引起的问题。当任务调用taskSafe( )时,从调用的那一刻起,该任务就被保护起来而不会被其它任务删除。如果任务1试图删除已经调用taskSafe( )的任务2,则任务1将被阻塞,直到任务2调用taskUn( )。保护只能由任务自己实现,一个任务不能或unsafe另外一个任务。taskSafe( )和taskUnsafe( )支持嵌套模式。如果有嵌套发生,一个计数器将开始工作,每有一个taskSafe( )被调用,则计数器加1;调用1个taskUnsafe( ),则计数器减1。只有当计数器为0时,才能删除该任务。
有时为了执行效率等原因,任务的运行需要禁止基于优先级的抢占,这可以通过调用taskLock( )实现。如果任务1调用taskLock( )禁止了高优先级任务对它的抢占,当任务1被阻塞或被暂停,核心将调度下一个具有最高优先级的就绪任务运行。如果这时任务1又就绪且被调度运行,抢占又被禁止。但是,禁止基于优先级的抢占可以阻止任务切换,却并不会屏蔽中断。调用taskUnLock( )可以解除优先级抢占的禁止,通过调用taskLock( )和taskUnLock( )可以实现对临界资源的互斥访问。
任务调度中CPU的占用
如前所述,不同优先级的任务是通过抢占获得CPU使用权的,如果不选时间片轮转,相同优先级的任务之间也是抢占CPU的。任务就绪队列中正在运行的任务如果不主动放弃CPU,则其它同优先级的任务不会得到运行,这样就有可能看到几个同优先级的任务状态同为READY,但实际上只有一个任务在运行的现象。比如在一个任务中用taskSpawn()函数创建一个同优先级或低优先级的任务,如果原任务一直占用CPU,新任务就不会开始运行。调用函数taskDelay()可以使任务放弃CPU一定的时间,从而实现任务间时间上的同步;也可以放弃CPU零时间,将任务移至同优先级就绪队列的末尾,这样就可以实现多个同优先级的任务并发运行。另外,由于中断能够打断任务的运行,中断处理函数中执行的代码就要尽可能少地占用CPU,并且中断中不能有获取信号量的操作。一旦处于等待之中,所有的任务均得不到运行,用户可能会有CPU不响应的错觉。
堆栈越界
如前所述,每一个任务都有自己的堆栈,任务创建时进行初始化。每个堆栈的大小是固定,但是任务运行过程中并不对堆栈的使用进行限制。由于VxWorks不对内存访问作限制,栈顶超越了原定的值后出现越界,这样操作系统中该任务堆栈以外的内存区域就可能被改写,会造成难以预料的结果,甚至可能造成任务的上下文区域被改写而任务消失。造成越界的原因主要是在函数中定义了比较大的数组,以致进栈时越界。这样在编写程序时,就要求在堆栈许可的范围内定义数组。如果确实需要比较大的内存空间,可以使用操作系统的内存分配函数来获得内存。由于堆栈越界后有可能使任务的控制信息被破坏,使得对堆栈越界的检测比较困难,例如可以在栈底写入一串特殊字符,用另外一个任务或者中断服务程序经常来检查是否被改写来判断越界。