RTOS系统CPU使用率和任务堆栈空间统计方法

在做产品开发时,我们都会接触到一个概念,那就是余量。对于硬件,比如某电容耐压至少需要15V,那么根据经验和设计规则,可能会留有一定的余量,最后选型耐压值为16V的电容。硬件如此,软件呢?更需要按照这种思路去设计,相比硬件,软件更无形,所以留有余量是衡量软件稳定性必须考量的一个关键点。
根据在整车软件设计的经验,CPU的负载率需要控制在85%以内,堆栈使用率控制在80%,对于外部通信,CAN的通信负载率需要控制在40%以内,最高不超过45%(硬件设计达到国际一流车企标准的前提下)。
OK,言归正传。我们在进行嵌入式软件设计时,如何去统计CPU负载率和任务堆栈使用情况呢?ucosII系统为开发测试提供了良好的接口,可以完成上述两项功能,如果是使用其他RTOS系统,如freeRTOS系统,没有提供类似接口,可以参照这种做法去自行实现。
下面列举出和使用率、任务堆栈统计相关的函数:
①OSInit(); //OS系统初始化

void  OSInit (void)
{
    OSInitHookBegin();                                           /* Call port specific initialization code   */

    OS_InitMisc();                                               /* Initialize miscellaneous variables       */

    OS_InitRdyList();                                            /* Initialize the Ready List                */

    OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */

    OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */

#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
    OS_FlagInit();                                               /* Initialize the event flag structures     */
#endif

#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
    OS_MemInit();                                                /* Initialize the memory manager            */
#endif

#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
    OS_QInit();                                                  /* Initialize the message queue structures  */
#endif

    OS_InitTaskIdle();                                           /* Create the Idle Task                     */
#if OS_TASK_STAT_EN > 0u
    OS_InitTaskStat();                                           /* Create the Statistic Task                */
#endif

#if OS_TMR_EN > 0u
    OSTmr_Init();                                                /* Initialize the Timer Manager             */
#endif

    OSInitHookEnd();                                             /* Call port specific init. code            */

#if OS_DEBUG_EN > 0u
    OSDebugInit();
#endif
}

其中,和我们话题相关的两条语句:

OS_InitTaskIdle();       /* Create the Idle Task                     */
OS_InitTaskStat();       /* Create the Statistic Task                */

空闲任务和统计任务的创建,里面就是创建了两个任务,这里就不过多粘贴代码,对应任务名称:OS_TaskIdle和OS_TaskStat。分别看下这两个任务:
②OS_TaskIdle()

void  OS_TaskIdle (void *p_arg)
{
#if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0u;
#endif
    p_arg = p_arg;                               /* Prevent compiler warning for not using 'p_arg'     */
    for (;;) {
        OS_ENTER_CRITICAL();
        OSIdleCtr++;
        OS_EXIT_CRITICAL();
        OSTaskIdleHook();                        /* Call user definable HOOK                           */
    }
}

空闲任务顾名思义,当系统没有其他任务需要执行的时候,会一直执行空闲任务,且每次执行时会对OSIdleCtr进行加1操作,这个变量后面会被用来计算CPU的使用率。在设置空闲任务时,将其优先级设置为次低,及LOW_PRIO-1,所以只要有其他任务需要执行,就轮不上这个任务,轮上这个任务执行,说明系统空闲了。
OSTaskIdleHook()函数,专业名称叫钩子函数,里面是空的,留有接口可以在应用层来实现一些功能。这样写法比较明显,容易被追踪到。在其他系统里,这种功能大多使用函数指针来实现回调。
③OS_TaskStat()

#if OS_TASK_STAT_EN > 0u
void  OS_TaskStat (void *p_arg)
{
#if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0u;
#endif
    p_arg = p_arg;                               /* Prevent compiler warning for not using 'p_arg'     */
    while (OSStatRdy == OS_FALSE) {
        OSTimeDly(2u * OS_TICKS_PER_SEC / 10u);  /* Wait until statistic task is ready                 */
    }
    OSIdleCtrMax /= 100uL;
    if (OSIdleCtrMax == 0uL) {
        OSCPUUsage = 0u;
#if OS_TASK_SUSPEND_EN > 0u
        (void)OSTaskSuspend(OS_PRIO_SELF);
#else
        for (;;) {
            OSTimeDly(OS_TICKS_PER_SEC);
        }
#endif
    }
    OS_ENTER_CRITICAL();
    OSIdleCtr = OSIdleCtrMax * 100uL;            /* Set initial CPU usage as 0%                        */
    OS_EXIT_CRITICAL();
    for (;;) {
        OS_ENTER_CRITICAL();
        OSIdleCtrRun = OSIdleCtr;                /* Obtain the of the idle counter for the past second */
        OSIdleCtr    = 0uL;                      /* Reset the idle counter for the next second         */
        OS_EXIT_CRITICAL();
        OSCPUUsage   = (INT8U)(100uL - OSIdleCtrRun / OSIdleCtrMax);
        OSTaskStatHook();                        /* Invoke user definable hook                         */
#if (OS_TASK_STAT_STK_CHK_EN > 0u) && (OS_TASK_CREATE_EXT_EN > 0u)
        OS_TaskStatStkChk();                     /* Check the stacks for each task                     */
#endif
        OSTimeDly(OS_TICKS_PER_SEC / 10u);       /* Accumulate OSIdleCtr for the next 1/10 second      */
    }
}
#endif

根据宏定义,缩减之后关键代码如下:

void  OS_TaskStat (void *p_arg)
{
    while (OSStatRdy == OS_FALSE) {
        OSTimeDly(2u * OS_TICKS_PER_SEC / 10u);  /* Wait until statistic task is ready                 */
    }
    for (;;) {
        OS_ENTER_CRITICAL();
        OSIdleCtrRun = OSIdleCtr;    /* Obtain the of the idle counter for the past second */
        OSIdleCtr    = 0uL;        /* Reset the idle counter for the next second  */
        OS_EXIT_CRITICAL();
        OSCPUUsage   = (INT8U)(100uL - OSIdleCtrRun / OSIdleCtrMax);
        OSTaskStatHook();          /* Invoke user definable hook       */
        OS_TaskStatStkChk();   /* Check the stacks for each task           */
        OSTimeDly(OS_TICKS_PER_SEC / 10u);       /* Accumulate OSIdleCtr for the next 1/10 second      */
    }
}
#endif

这个函数就很明显,计算了CPU使用率OSCPUUsage。
任务栈空间使用的统计,在函数OS_TaskStatStkChk();中,在此函数中会对所有任务进行遍历,并在OS_TaskStatStkChk()下一级函数OSTaskStkChk()统计出每个栈空间free_size的大小,然后用total_size减去free_size就是已使用的空间。关键统计部分如下:

 while (*pchk++ == (OS_STK)0) {  /* Compute the number of zero entries on the stk */
        nfree++;
    }
 p_stk_data->OSFree = nfree; /* Store   number of free entries on the stk     */
 p_stk_data->OSUsed = size - nfree; /* Compute number of entries used on the stk     */

很显然,在while中从栈的基地址往栈底进行统计,如果为0则认为是free的空间,直到发现非0空间,才停止统计。然后用free和总的size来计算OSUsed已使用空间。这个统计方法比较实用,但如果很较真的话,这个统计有点弊端,假如一个数组被压如栈中,buf[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2};按照存储规则,如下图,统计出来会有误差,所以这个统计只能作为参考值,在参考值之外需要留有一定余量。并且在统计过程中,需要对各种可能发生的情况进行测试,才能得到比较接近的值。
在这里插入图片描述
④OSStatInit ()

void  OSStatInit (void)
{
#if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0u;
#endif
    OSTimeDly(2u);                               /* Synchronize with clock tick                        */
    OS_ENTER_CRITICAL();
    OSIdleCtr    = 0uL;                          /* Clear idle counter                                 */
    OS_EXIT_CRITICAL();
    OSTimeDly(OS_TICKS_PER_SEC / 10u);           /* Determine MAX. idle counter value for 1/10 second  */
    OS_ENTER_CRITICAL();
    OSIdleCtrMax = OSIdleCtr;                    /* Store maximum idle counter count in 1/10 second    */
    OSStatRdy    = OS_TRUE;
    OS_EXIT_CRITICAL();
}
#endif

最后,如何开启这个统计功能呢?那就需要调用该初始化函数了。好了,到此为止,CPU使用率统计和任务栈使用统计就能够实现了。
在代码正式上线时,需要把该任务关闭,即将相关的宏定义定义为0即可,以节省资源。

转载请注明出处,如有错误,欢迎指正!

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值