16. CPU使用率统计¶
16.1. CPU利用率的基本概念¶
CPU使用率其实就是系统运行的程序占用的CPU资源,表示机器在某段时间程序运行的情况,如果这段时间中, 程序一直在占用CPU的使用权,那么可以人为CPU的利用率是100%。CPU的利用率越高, 说明机器在这个时间上运行了很多程序,反之较少。利用率的高低与CPU强弱有直接关系,就像一段一模一样的程序, 如果使用运算速度很慢的CPU,它可能要运行1000ms,而使用很运算速度很快的CPU可能只需要10ms,那么在1000ms这段时间中, 前者的CPU利用率就是100%,而后者的CPU利用率只有1%,因为1000ms内前者都在使用CPU做运算, 而后者只使用10ms的时间做运算,剩下的时间CPU可以做其他事情。
FreeRTOS是多任务操作系统,对 CPU 都是分时使用的:比如A任务占用10ms,然后B任务占用30ms,然后空闲60ms, 再又是A任务占10ms,B任务占30ms,空闲60ms;如果在一段时间内都是如此,那么这段时间内的利用率为40%, 因为整个系统中只有40%的时间是CPU处理数据的时间。
16.2. CPU利用率的作用¶
一个系统设计的好坏,可以使用CPU使用率来衡量,一个好的系统必然是能完美响应急需的处理, 并且系统的资源不会过于浪费(性价比高)。举个例子,假设一个系统的CPU利用率经常在90%~100%徘徊, 那么系统就很少有空闲的时候,这时候突然有一些事情急需CPU的处理,但是此时CPU都很可能被其他任务在占用了, 那么这个紧急事件就有可能无法被相应,即使能被相应,那么占用CPU的任务又处于等待状态,这种系统就是不够完美的, 因为资源处理得太过于紧迫;反过来,假如CPU的利用率在1%以下,那么我们就可以认为这种产品的资源过于浪费, 搞一个那么好的CPU去干着没啥意义的活(大部分时间处于空闲状态),使用,作为产品的设计,既不能让资源过于浪费, 也不能让资源过于紧迫,这种设计才是完美的,在需要的时候能及时处理完突发事件,而且资源也不会过剩,性价比更高。
16.3. CPU利用率统计¶
FreeRTOS是一个很完善很稳定的操作系统,当然也给我们提供测量各个任务占用CPU时间的函数接口, 我们可以知道系统中的每个任务占用CPU的时间,从而得知系统设计的是否合理,出于性能方面的考虑, 有的时候,我们希望知道CPU的使用率为多少,进而判断此CPU的负载情况和对于当前运行环境是否能够“胜任工作”。 所以,在调试的时候很有必要得到当前系统的CPU利用率相关信息,但是在产品发布的时候,就可以把CPU利用率统计这个功能去掉, 因为使用任何功能的时候,都是需要消耗系统资源的,FreeRTOS 是使用一个外部的变量进行统计时间的, 并且消耗一个高精度的定时器,其用于定时的精度是系统时钟节拍的10-20倍,比如当前系统时钟节拍是1000HZ, 那么定时器的计数节拍就要是10000-20000HZ。而且FreeRTOS进行CPU利用率统计的时候,也有一定缺陷, 因为它没有对进行CPU利用率统计时间的变量做溢出保护,我们使用的是 32位变量来系统运行的时间计数值, 而按20000HZ的中断频率计算,每进入一中断就是50us,变量加一,最大支持计数时间:2^32 * 50us / 3600s =59.6 分钟, 运行时间超过了 59.6 分钟后统计的结果将不准确,除此之外整个系统一直响应定时器50us一次的中断会比较影响系统的性能。
用户想要使用CPU利用率统计的话,需要自定义配置一下,在STM32CubeIDE中使能以下三个选项
通过上面的配置将会在FreeRTOSConfig.h文件中设置以下三个宏为1。
1 2 3 | #define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
|
然后需要实现一个中断频率为20000HZ定时器,用于系统运行时间统计,其实配置如下
在定时器中断周期回调函数中只需将CPU_RunTime变量自加即可,这个变量是用于记录系统运行时间的, 其代码如下所示
1 2 3 4 5 6 7 8 9 10 | volatile unsigned long CPU_RunTime = 0UL;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6 )
{
CPU_RunTime++;
}
}
|
使用CPU利用率还需要实现configureTimerForRunTimeStats与getRunTimeCounterValue函数,其函数内容相对简单。
1 2 3 4 5 6 7 8 9 | void configureTimerForRunTimeStats(void)
{
CPU_RunTime = 0UL;
}
unsigned long getRunTimeCounterValue(void)
{
return CPU_RunTime;
}
|
16.4. CPU利用率统计实验¶
CPU利用率实验是在FreeRTOS中创建了三个线程,其中两个线程是普通线程, 另一个线程用于获取CPU利用率与任务相关信息并通过串口打印出来。其线程配置如下所示
本小节只讲解重点部分代码,完整代码请打开工程查看。
16.4.1. MX_FREERTOS_Init函数¶
MX_FREERTOS_Init函数由STM32CubeIDE生成,代码如下所示
1 2 3 4 5 6 7 | void MX_FREERTOS_Init(void) {
LED1TaskHandle = osThreadNew(LED1_Task, NULL, &LED1Task_attributes);
LED2TaskHandle = osThreadNew(LED2_Task, NULL, &LED2Task_attributes);
CPUTaskHandle = osThreadNew(CPU_Task, NULL, &CPUTask_attributes);
}
|
16.4.2. LED1_Task线程¶
1 2 3 4 5 6 7 8 9 10 11 12 | void LED1_Task(void *argument)
{
for(;;)
{
LED1_ON;
osDelay(500); /* 延时500个tick */
printf("LED1_Task Running,LED1_ON\r\n");
LED1_OFF;
osDelay(500); /* 延时500个tick */
printf("LED1_Task Running,LED1_OFF\r\n");
}
}
|
16.4.3. LED2_Task线程¶
1 2 3 4 5 6 7 8 9 10 11 12 13 | void LED2_Task(void *argument)
{
for(;;)
{
LED2_ON;
osDelay(300); /* 延时500个tick */
printf("LED2_Task Running,LED1_ON\r\n");
LED2_OFF;
osDelay(300); /* 延时500个tick */
printf("LED2_Task Running,LED1_OFF\r\n");
}
}
|
16.4.4. CPU利用率相关线程¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | uint8_t CPU_RunInfo[400]; //保存任务运行时间信息
void CPU_Task(void *argument)
{
//启动定时器
HAL_TIM_Base_Start_IT(&htim6);
for(;;)
{
memset(CPU_RunInfo,0,400); //信息缓冲区清零
vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息
printf("---------------------------------------------\r\n");
printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
memset(CPU_RunInfo,0,400); //信息缓冲区清零
vTaskGetRunTimeStats((char *)&CPU_RunInfo);
printf("任务名 运行计数 使用率\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n\n");
osDelay(1000); /* 延时500个tick */
}
}
|
16.4.5. main函数¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | int main(void)
{
HAL_Init();
if(IS_ENGINEERING_BOOT_MODE())
{
SystemClock_Config();
}
MX_GPIO_Init(); //初始化GPIO
MX_USART3_UART_Init(); //初始化串口
MX_TIM6_Init(); //初始化定时器
osKernelInitialize(); //初始化内核状态
MX_FREERTOS_Init(); //创建任务
osKernelStart(); //启动任务调度器,
while (1)
{
}
}
|
16.5. CPU利用率统计实验现象¶
当程序运行会在串口中不断打印