forgot 发表于 2023-2-21 16:27

uC/OS-II中的任务堆栈大小检测

本帖最后由 forgot 于 2023-2-21 16:30 编辑

在uC/OS-II系统中,创建任务的时候除了需要配置任务的优先级以外,还需要对任务堆栈大小进行分配。而对于单片机这种RAM并不是非常大的微控制器来说,RAM显得格外珍贵,如果任务堆栈分配大了,会导致RAM不够用,分配小了任务运行又会受到影响,甚至系统的崩溃,所以在创建任务的时候合适的堆栈大小分配很重要。
uC/OS-II中任务堆栈的作用主要有两个,一是在任务运行的时候保存任务的一些局部变量,二是当任务挂起时,保存任务现场,也就是CPU寄存器的值。uC/OS-II系统是在OSTaskCreate();中的OSTaskStkInit();来确定堆栈地址的。虽然任务堆栈大小也可以通过设计者认为去测算出来,但是要考虑的东西太多,而且也不能十分精确。
实际上uC/OS-II在OS_TASK.C中提供了很多有关任务管理的功能函数,包括:建立任务OSTaskCreate()、建立扩展任务OSTaskCreateExt()、删除任务OSTaskDel()等,其中还有一个关于堆栈的函数就是堆栈检验OSTaskStkChk(),这个函数的检测原理就是顺着堆栈的栈底开始计算空闲的堆栈空间大小,实现方法是统计存储值为0的连续堆栈入口的数量,直到发现存储值不为0的堆栈入口,这样就可以知道实际占用的堆栈大小了。
该函数的调用和运行需要将OS_TASK_STAT_STK_CHK_EN和OS_TASK_CREATE_EXT_EN进行使能,而检测的任务对象是通过任务的优先级来获取的,即:INT8Uprio,并且需要定义一个OS_STK_DATA的结构体变量来存放堆栈情况的值,其中的:INT32UOSFree    就是剩余的堆栈大小INT32UOSUsed    是已经使用的堆栈大小。
OSTaskStkChk()源码:INT8UOSTaskStkChk (INT8U         prio,
                     OS_STK_DATA*p_stk_data)
{
    OS_TCB    *ptcb;
    OS_STK    *pchk;
    INT32U   nfree;
    INT32U   size;
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register   */
    OS_CPU_SRcpu_sr = 0u;
#endif



#if OS_ARG_CHK_EN > 0u
    if (prio > OS_LOWEST_PRIO) {                     /* Make sure task priority is valid             */
      if (prio != OS_PRIO_SELF) {
            return (OS_ERR_PRIO_INVALID);
      }
    }
    if (p_stk_data == (OS_STK_DATA *)0) {            /* Validate 'p_stk_data'                        */
      return (OS_ERR_PDATA_NULL);
    }
#endif
    p_stk_data->OSFree = 0u;                           /* Assume failure, set to 0 size                */
    p_stk_data->OSUsed = 0u;
    OS_ENTER_CRITICAL();
    if (prio == OS_PRIO_SELF) {                        /* See if check for SELF                        */
      prio = OSTCBCur->OSTCBPrio;
    }
    ptcb = OSTCBPrioTbl;
    if (ptcb == (OS_TCB *)0) {                         /* Make sure task exist                         */
      OS_EXIT_CRITICAL();
      return (OS_ERR_TASK_NOT_EXIST);
    }
    if (ptcb == OS_TCB_RESERVED) {
      OS_EXIT_CRITICAL();
      return (OS_ERR_TASK_NOT_EXIST);
    }
    if ((ptcb->OSTCBOpt & OS_TASK_OPT_STK_CHK) == 0u) { /* Make sure stack checking option is set      */
      OS_EXIT_CRITICAL();
      return (OS_ERR_TASK_OPT);
    }
    nfree = 0u;
    size= ptcb->OSTCBStkSize;
    pchk= ptcb->OSTCBStkBottom;
    OS_EXIT_CRITICAL();
#if OS_STK_GROWTH == 1u
    while (*pchk++ == (OS_STK)0) {                  /* Compute the number of zero entries on the stk */
      nfree++;
    }
#else
    while (*pchk-- == (OS_STK)0) {
      nfree++;
    }
#endif
    p_stk_data->OSFree = nfree * sizeof(OS_STK);          /* Compute number of free bytes on the stack */
    p_stk_data->OSUsed = (size - nfree) * sizeof(OS_STK); /* Compute number of bytes used on the stack */
    return (OS_ERR_NONE);
}
对于需要检测的目标任务来说,需要采用OSTaskCreateExt()扩展任务来创建的,区别与普通的OSTaskCreate();函数,创建扩展任务不仅需要指定堆栈的底指针,还指定了堆栈的容量,这个容量值就是用来检验使用的,而该函数的最后一个参数opt 填入OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR即可。
OSTaskCreateExt ()源码:INT8UOSTaskCreateExt (void   (*task)(void *p_arg),
                        void    *p_arg,
                        OS_STK*ptos,
                        INT8U    prio,
                        INT16U   id,
                        OS_STK*pbos,
                        INT32U   stk_size,
                        void    *pext,
                        INT16U   opt)
{
    OS_STK    *psp;
    INT8U      err;
#if OS_CRITICAL_METHOD == 3u               /* Allocate storage for CPU status register               */
    OS_CPU_SRcpu_sr = 0u;
#endif



#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
      OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (prio > OS_LOWEST_PRIO) {             /* Make sure priority is within allowable range         */
      return (OS_ERR_PRIO_INVALID);
    }
#endif
    OS_ENTER_CRITICAL();
    if (OSIntNesting > 0u) {               /* Make sure we don't create the task from within an ISR*/
      OS_EXIT_CRITICAL();
      return (OS_ERR_TASK_CREATE_ISR);
    }
    if (OSTCBPrioTbl == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority*/
      OSTCBPrioTbl = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ...*/
                                             /* ... the same thing until task is created.            */
      OS_EXIT_CRITICAL();

#if (OS_TASK_STAT_STK_CHK_EN > 0u)
      OS_TaskStkClr(pbos, stk_size, opt);                  /* Clear the task stack (if needed)   */
#endif

      psp = OSTaskStkInit(task, p_arg, ptos, opt);         /* Initialize the task's stack          */
      err = OS_TCBInit(prio, psp, pbos, id, stk_size, pext, opt);
      if (err == OS_ERR_NONE) {
            if (OSRunning == OS_TRUE) {                        /* Find HPT if multitasking has started */
                OS_Sched();
            }
      } else {
            OS_ENTER_CRITICAL();
            OSTCBPrioTbl = (OS_TCB *)0;                  /* Make this priority avail. to others*/
            OS_EXIT_CRITICAL();
      }
      return (err);
    }
    OS_EXIT_CRITICAL();
    return (OS_ERR_PRIO_EXIST);
}
这就是uC/OS-II自带检验任务堆栈大小的方法,但是由于任务运行受实际产品使用状态等很多方面的影响,必须在任务最大运行负载的情况下所测出来的使用率才是最可靠的,一般为了防止堆栈过小,在RAM空间够用的时候,建议最后可以将实际测出来的大小*2进行堆栈分配。

页: [1]
查看完整版本: uC/OS-II中的任务堆栈大小检测