全部博文(86)
分类: 嵌入式
2017-11-19 12:14:33
任务优先级分配
有时候任务的优先级是显而易见的。例如,如果嵌入式系统中最重要的方面是要进行某种控制,并且已知控制算法必须响应,那么最好把控制任务分配到高优先级,而显示和操作员的接口任务是低优先级的。然而,大多数情况下,由于实时系统的复杂性,分配任务的优先级并不是那么简单。在大多数系统中,不是所有的任务都被认为是关键的,而非关键任务显然应该被给予低优先级。
作为系统设计的一条准则,CPU使用率应低于60%~70%。
任务堆栈大小的确定
任务堆栈的大小取决于应用的需求。然而,在对堆栈进行分级时,必须考虑到任务调用的所有函数的嵌套,由任务调用的所有函数分配的局部变量的数量,以及所有嵌套中断服务例程的堆栈要求。此外,如果处理器有FPU,栈必须能够存储所有的CPU寄存器和可能的浮点单元(FPU)寄存器。在嵌入式系统中,避免编写递归代码。
可以手动计算所需的堆栈空间,把所有函数调用嵌套所需的所有内存(每个调用函数返回一个地址指针),加上所有在函数调用中传递的参数所需要的所有内存,加上上下文切换时所有CPU寄存器空间(大小依赖于CPU),加上处理每个嵌套的ISRs需要的所有CPU寄存器空间(如果CPU没有单独的堆栈来处理ISRs),加上ISRs所需要的堆栈空间。把所有这些加起来是一项繁琐的工作,得到的结果是最小的需求空间。通常再乘以1.5到2.0以确保任务的堆栈够用。这个计算假定代码的精确路径在任何时候都是已知的,这并不总是可能的。特别是,当调用函数如printf()或其他库函数时,很难甚至几乎不可能猜到printf()需要多少堆栈空间。在这种情况下,从一个相当大的堆栈空间开始,并在运行时监视堆栈使用情况,看看应用程序运行一段时间后实际使用了多少堆栈空间。
有非常酷和聪明的编译器/链接器在一个链接映射(link map)中提供这些信息。对于每个函数,链接映射给出最糟糕的堆栈使用情况。这个特性使您能够更好地评估每个任务的堆栈使用情况。此外加上CPU上下文切换所需的堆栈空间,加上每个嵌套的IS进行 CPU上下文切换所需堆栈空间(如果CPU没有单独的堆栈来处理ISRs),以及那些ISRs需要的堆栈空间。为了安全,在此基础上再乘以1.5到2.0。在开发和测试产品时,总是监视堆栈的使用,因为堆栈溢出经常发生,并可能导致一些奇怪的问题。事实上,每当有人提到他或她的应用程序行很“奇怪”时,首先想到的就是堆栈大小不足。
检测任务堆栈溢出
1、 使用MMU或者MPU
如果处理器有内存管理单元(MMU)或者内存保护单元(MPU),可以很容易检查堆栈益处。MMU和MPU是CPU集成的特殊硬件设备,可以配置为检测任务何时试图访问无效的内存位置,无论是代码、数据还是堆栈。
2、 使用CPU的堆栈溢出检测功能(部分CPU有)
有些处理器确实有简单的堆栈指针溢出检测寄存器。当CPU的堆栈指针小于(或大于依赖于堆栈增长方式)时,设置该寄存器的值,并产生异常中断,异常处理程序确保代码不会造成进一步损害(可能会对错误的代码发出警告,甚至终止它)。OS_TCB中的.SthLimutPtr就是为了这个目的而设计的。注意,堆栈限制的位置通常设置在任务堆栈的有效位置,而堆栈上留有足够的空间来处理它本身的异常 (假设CPU没有单独的异常堆栈)。在大多数情况下,这个位置可以相当接近&MyTaskStk[0]。
注意.SthLimutPtr由OSTaskCreate()传入的stk_limit参数决定。如下:
当然,当uC/OS-III进行上下文切换时,用于CPU硬件堆栈检测的.SthLimutPtr值是改变的。这可能很棘手,因为这个寄存器的值可能需要更改,它首先指向NULL,然而CPU的堆栈指针是变化的更改CPU堆栈指针,最后将堆栈检查寄存器的值设置为在TCB 的. stklimitptr中保存的值。为什么?因为如果不遵循序列,在堆栈指针或堆栈溢出检测寄存器更改时产生异常。通过首先改变堆栈溢出检测寄存器指向指向确保堆栈指针永远无效的位置。
3、 基于自定义软件的堆栈溢出检测
当uC/OS-III进行任务切换时,会调用hook函数(OSTaskSwHook()),它允许用户扩展上下文切换功能。所以如果处理器没有硬件堆栈溢出检测功能,可以把堆栈溢出检测功能代码加到OSTaskSwHook()。特别的,在转换任务之前,代码应该确保将堆栈指针加载到CPU中不超过放在. stklimitptr中的限制。因为软件不能在堆栈溢出时迅速做出反应。.stklimitptr的值尽量远离&MyTaskStk[0]是非常重要的。像这样的软件实现不像基于硬件的检测机制那么可靠,但仍然可以防止可能的堆栈溢出。如下图:
4、 Redzone堆栈溢出检测
Redzone堆栈溢出检测机制是内置在uC/OS-III。这种基于软件的方法实现了前一节中定义的内容,设置oss_cfg.h配置文件中的OS_CFG_TASH_STK_REDZONE_EN使能该功能。使能该功能时,uC/OS-III会在任务堆栈的末端创建一块监控区域,叫做红色区域(Redzone)。在创建任务时,用特殊的值来填充这块区域。
5、 计算空闲堆栈空间的数量
另一种检查堆栈溢出的方法是分配比预期更多的堆栈空间,然后在运行时监视并可能显示实际的最大堆栈使用情况。这很容易做到。首先创建任务时对其堆栈进行清除(例如填充0)。然后通过一个低优先级的任务,处理被创建的任务的堆栈,计算从&MyTaskStk[0]底部到顶部为零的空间大小。当该任务找到一个非零值时,该进程将被停止,并且可以计算堆栈的使用(使用的字节数或百分比)。然后,您可以调整堆栈的大小(通过重新编译代码)来分配更合理的值(要么增加或减少每个任务的堆栈空间的数量)。然而,为了使其有效,您需要运行足够长的应用程序,使堆栈能够增长到最高的值。如下图所示。uC/OS-III提供了一个在运行时执行这个计算的函数OSTaskStkChk(),而实际上,这个函数是由OS_StatTask()调用的,用于计算应用程序中创建的每个任务的堆栈使用情况。