首页 > 其他分享 >FreeRTOS系统中CPU使用率统计方法分析

FreeRTOS系统中CPU使用率统计方法分析

时间:2023-11-16 22:56:28浏览次数:40  
标签:RUN FreeRTOS 任务 TIME STATS 使用率 CPU

基本概念

操作系统中CPU使用率是在软件架构设计中必须要考虑的一个重要性能指标。它直接影响到程序的执行时间以及优先级更高的任务能否实时响应的问题。而CPU使用率也不能过低,避免资源浪费。

统计方法

FreeRTOS操作系统是使用任务的累计运行时间来统计每一个任务自系统开始运行到当前时刻的CPU占用时间,即该任务的CPU使用率。比如:系统上电到当前时刻一共运行了100s,其中任务A运行了1s,任务B运行了2s,剩下的97s由空闲任务在运行,那么在10s的时间内,任务A的CPU使用率是1%,任务B的CPU使用率是2%,空闲任务的CPU使用率是97%。

下图是FreeRTOS系统CPU使用率统计示意图:

 

源码分析1

FreeRTOS系统中CPU使用率统计功能是一个可裁剪的功能,可通过宏configGENERATE_RUN_TIME_STATS来进行裁剪,代码如下:
FreeRTOSConfig.h

/* 使能CPU使用率统计功能 */
#define configGENERATE_RUN_TIME_STATS   0

而如果使能了CPU使用率统计功能,则在每一个任务的任务控制块中,定义了一个变量ulRunTimeCounter用于统计该任务的运行时间,代码如下:
task.c

/* 任务控制块接口体定义 */
typedef struct tskTaskControlBlock{
    volatile StackType_t   *pxTopOfStack;
    // ...
#if( configGENERATE_RUN_TIME_STATS == 1 )
    uint32_t        ulRunTimeCounter;   /*< Stores the amount of time the task has spent in the Running state. */
#endif
    // ...
}

在进行任务上下文切换时,会将当前任务的本次运行时间(从上一次任务上下文切换开始至当前时刻)累计到当前任务的任务控制块中的ulRunTimeCounter上,任务上下文切换的代码如下:
task.c

void vTaskSwitchContext( void ){
    if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    {        
        /* The scheduler is currently suspended - do not allow a context        switch. */
        xYieldPending = pdTRUE;
    }
    else
    {
       xYieldPending = pdFALSE;
        traceTASK_SWITCHED_OUT();

       /* 条件编译选项:如果使能了CPU使用率统计功能,则下面的代码用于统计当前任务的CPU占用总时间 */
        #if ( configGENERATE_RUN_TIME_STATS == 1 )
        {
            /* **(1)获取当前时间ulTotalRunTime(CPU运行的总时间)** */
            #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
                portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
           #else
               ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
           #endif
               /* **(2)将当前任务的运行时间(当前时间(ulTotalRunTime) - 上一次任务切换的时间(ulTaskSwitchedInTime))累加到当前任务控制块** */
                if( ulTotalRunTime > ulTaskSwitchedInTime )
                {
                    pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                } 
                /* **(3)更新上一次任务切换的时间ulTaskSwitchedInTime,作为下一次任务切换时计算任务运行时间的基准时刻** */
                ulTaskSwitchedInTime = ulTotalRunTime; 
           } 
       #endif /* configGENERATE_RUN_TIME_STATS */

        /* Check for stack overflow, if configured. */
        taskCHECK_FOR_STACK_OVERFLOW();

        /* Select a new task to run using either the generic C or port        optimised asm code. */
        taskSELECT_HIGHEST_PRIORITY_TASK();
        traceTASK_SWITCHED_IN();
        #if ( configUSE_NEWLIB_REENTRANT == 1 ) 
       {
                  /* Switch Newlib's _impure_ptr variable to point to the _reent            structure specific to this task. */
                  _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
        }       
        #endif /* configUSE_NEWLIB_REENTRANT */
    }
}

由上面的代码可知,要实现任务的CPU使用率统计功能,还需要实现宏函数portGET_RUN_TIME_COUNTER_VALUE()用于获取当前时间,这里有两种方式:
(1)使用系统时基(一般取1ms)作为当前时间,这种方式仅用于粗略估算CPU使用率,代码如下:
FreeRTOSConfig.h

/* Run time stats gathering definitions. */
#define configGENERATE_RUN_TIME_STATS  1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
#define portGET_RUN_TIME_COUNTER_VALUE() xTaskGetTickCount()    /* 获取系统tick */

(2)使用高精度定时器(一般精度在50us以上,即系统时钟节拍的10~20倍)作为当前时间,这种方式计算的CPU使用率相对准确,代码如下:
FreeRTOSConfig.h

/* Run time stats gathering definitions. */
#define configGENERATE_RUN_TIME_STATS  1
extern volatile uint32_t ulHighFrequencyTimerCounts;    /* 在高精度定时器中断中累加 */
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()    (ulHighFrequencyTimerCounts = 0ul)
#define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerCounts

然后在定时器中断处理程序中对变量ulHighFrequencyTimerCounts进行累加,代码如下
IT_Timer.c

/* 用于统计CPU运行时间 */
volatile uint32_t ulHighFrequencyTimerCounts = 0ul;
/* 高精度定时器中断服务函数(定时周期50us) */
void TIM_IRQ_Handler(void)
{
    // 判断中断标志位
    ulHighFrequencyTimerCounts++;
    // 清除中断标志
}

用户使用方法

用户可以通过调用 vTaskList() 和 vTaskGetRunTimeStats() 函数获取任务的相关信息和CPU使用率的相关信息,然后打印出来即可,使用代码如下:
user_monitor.c

void DumpTaskSysFree(void)
{
    uint8_t CPU_RunInfo[400] = {0};

    memset(CPU_RunInfo, 0, 400);
    vTaskList((char *)&CPU_RunInfo);
    printf("任务名  任务状态  优先级  剩余栈  任务序号\r\n ");
    printf("%s\r\n", CPU_RunInfo);
   
    memset(CPU_RunInfo,0,400); 
    vTaskGetRunTimeStats((char *)&CPU_RunInfo); 
    printf("任务名  运行计数  使用率 \r\n ");
    printf("%s\r\n", CPU_RunInfo);
}

而要启用 vTaskList() 和 vTaskGetRunTimeStats() 函数,还需要在FreeRTOSConfig.h中添加以下配置
FreeRTOSConfig.h

/* 启用可视化跟踪调试 */
#define configUSE_TRACE_FACILITY 1 
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 

源码分析2

下面一起来分析以下vTaskGetRunTimeStats() 函数源码(vTaskList()函数分析过程大致相同,这里不再赘述):
task.c

#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) )
    void vTaskGetRunTimeStats( char *pcWriteBuffer )
    {
        TaskStatus_t *pxTaskStatusArray;
        volatile UBaseType_t uxArraySize, x;
        uint32_t ulTotalTime, ulStatsAsPercentage;
        #if( configUSE_TRACE_FACILITY != 1 )
        {
            #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats().
        }
        #endif
        
        /* 确保 write buffer中没有字符串(写入字符串结束符). */
        *pcWriteBuffer = 0x00;
        /* 获取当前系统任务个数 */
        uxArraySize = uxCurrentNumberOfTasks;
        /* 为每一个任务申请一块内存空间用于存储任务状态信息. */
        pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) );
        if( pxTaskStatusArray != NULL )
        {
            /* Generate the (binary) data. */
            uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime );
            /* ulTotalTime为系统运行总时间. */
            ulTotalTime /= 100UL;
            /* Avoid divide by zero errors. */
            if( ulTotalTime > 0 )
            {
                /* Create a human readable table from the binary data. */
                for( x = 0; x < uxArraySize; x++ )
                {
                    /* 计算当前任务的CPU占用率. */
                    ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime;
                    /* 将任务名写入到输出字符串. */
                    pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName );
                    /* 将当前任务的运行时间ulRunTimeCounter和CPU占用率写入到输出字符串 */
                    if( ulStatsAsPercentage > 0UL )
                    {
                    #ifdef portLU_PRINTF_SPECIFIER_REQUIRED
                    {
                        sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
                    }
                    #else
                    {
                        /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */
                        sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage );
                    }
                    #endif
                    }
                    else
                    {
                    /* 如果计算的CPU占用率为0, 则用"<1%"表示 */
                    #ifdef portLU_PRINTF_SPECIFIER_REQUIRED
                    {
                        sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter );
                    }
                    #else
                    {
                        /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */
                        sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter );
                    }
                    #endif
                }
                pcWriteBuffer += strlen( pcWriteBuffer );
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
        /* 释放内存块pxTaskStatusArray . */
        vPortFree( pxTaskStatusArray );
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}
#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */

简单来说,vTaskGetRunTimeStats() 函数的作用就是通过当前任务的运行时间ulRunTimeCounter和系统运行总时间ulTotalTime 来计算当前任务的CPU占用率,并格式化输出。

优缺点

(1)FreeRTOS统计CPU占用时间使用的是一个32bit无符号整形来记录系统时间的,而统计时间时没有对统计时间的变量做溢出保护,当定时器精度为50us时,32bit无符号整形支持的最大计数时间为:
Tmax = 2^32 * 50us/1000100060=59.6分钟
当运行时间超过59.6分钟时,统计结果将不准确。
(2)高精度定时器频繁的进入中断也会对系统性能造成一定的影响。

 

标签:RUN,FreeRTOS,任务,TIME,STATS,使用率,CPU
From: https://www.cnblogs.com/god-of-death/p/17837474.html

相关文章

  • FreeRTOS(3):事件组、任务通知
    1.事件组学校组织秋游,组长在等待:⚫张三:我到了⚫李四:我到了⚫王五:我到了⚫组长说:好,大家都到齐了,出发!秋游回来第二天就要提交一篇心得报告,组长在焦急等待:张三、李四、王五谁先写好就交谁的。在这个日常生活场景中:⚫出发:要等待这3个人都......
  • 浪潮NF5280M6扩容内存出现CPU0_C1D0 Memory Device Disabled Memory
    浪潮NF5280M6服务器32G*8根(256G)内存扩容至12根(384G)扩容后提示内存告警查看内存详情在位12根,内存总容量320G,不是384G。查看系统日志:CPU0_C5D0MemoryDeviceDisabledMemory(CPU0-CH5-DIMM0)Disabled:DisabledRankDetail(MFR:NOTFOUND,PN:NOTFOUND,SN:NOTFOUND)-AssertCPU0_C5......
  • 爆款元服务!教你如何设计高使用率卡片
     元服务的概念相信大家已经在HDC 2023上有了很详细的了解,更轻便的开发方式,让开发者跃跃欲试。目前也已经有很多开发者开发出了一些爆款元服务,那么如何让你的元服务拥有更高的传播范围、更高的用户使用率和更多的用户触点呢?设计一张好的卡片是你的不二之选。那么如何设计一张......
  • 808-ORI-D3R600服务器-多路PCIe3.0的双CPU通用工作站
      一、机箱功能和技术指标:系统系统型号ORI-SR630主板支持EEB(12'*13')/CEB(12'*10.5')/ATX(12'*9.6')/MicroATX前置硬盘最大支持8个3.5寸(兼容25寸)SATA硬盘+2*2.5(后置)电源类型CRPS元余电源,标准ATX电源散热系统......
  • 807-ORI-S3R500 -多路PCIe3.0的单CPU通用工作站
    ORI-S3R500-多路PCIe3.0的单CPU通用工作站  (研华工业计算机IPC-610,IPC940 升级款)    一、机箱功能和技术指标:系统系统型号ORI-SR500主板支持EEB(12'*13')/CEB(12'*10.5')/ATX(12'*9.6')/MicroATX前置硬盘最大支持......
  • k8s中是如何计算pod的计算资源(cpu和内存)的requests和limits值的?
    1、结论 对于pod来说,cpu和内存的requests和limits的值,等于pod中所有容器的requests和limits的值的总和。 具体来说:pod的requests的大小,等于所有的容器的requests的大小的和,如果某个容器没有设置requests,则这个值是0.pod的limits的值的大小,等于所有容器的limits的值的大......
  • linux查看cpu数
    查看cpu个数 cat/proc/cpuinfo|grep"physicalid"|sort|uniq|wc-l查看cpu信息cat/proc/cpuinfo 因为从0开始编号所以有24个核心。Intel(R)Xeon(R)[email protected]单看名字只能知道它是6核12线程的。......
  • 新发布的Java使用率均超Java8
    Java软件供应商Azul发布了首份年度Java现状调查报告,基于对全球2062名Java专业人士和基于Java的应用程序用户进行的调查。Java软件供应商Azul发布了首份年度Java现状调查报告,基于对全球2062名Java专业人士和基于Java的应用程序用户进行的调查。调查探......
  • 【故障公告】数据库服务器今年第六次 CPU 100% 故障
    自9月第五次数据库服务器CPU100%故障之后,今天下午又出现数据库服务器CPU100%故障,是今年的第六次。自从园子2013年搬上阿里云,几乎每年都会遇到数据库服务器CPU100%问题,但今年创造了新记录,一年还未结束,却已遭遇六次,最困难的一年,连故障也过来凑热闹。今天的故障发生于14......
  • 1. 手动移植FreeRTOS V9.00到 Stm32F103C8T6
    记录移植过程,以便以后查看:1.使用cubeMAX配置一个工程a.选择单片机型号: b。设置SWD调试,选TIM4作为系统时钟,systick要用来跑freeRTOS c.NVIC默认使用Group4 配置GPIO,我这里配置了两个引脚,作为LED输出 d,RCC都选外部晶振 e。配置时钟树,HCLK72Mhz  f。project......