[应用相关] 一种计算 CPU 使用率的方法及其实现原理

[复制链接]
2643|19
 楼主| 我想看大海 发表于 2021-11-9 10:22 | 显示全部楼层 |阅读模式
1 前言
出于性能方面的考虑,有的时候,我们希望知道 CPU 的使用率为多少, 进而判断此 CPU 的负载情况和对于当前运行环境是
否足够“ 胜任” 。本文将介绍一种计算 CPU 占有率的方法以及其实现原理。  

 楼主| 我想看大海 发表于 2021-11-9 10:22 | 显示全部楼层
2 移植算法
2.1 算法简介
此算法是基于操作系统的, 理论上不限于任何操作系统,只要有任务调度就可以。 本文将以 FreeRTOST 为例来介绍本算法的
使用方法。
本文所介绍的算法出处为随 Cube 库一起提供的, 它在 cube 库中的位置如下图所示:  

248766189db7359682.png
本文将以 STM32F4 为例, 测试环境为 STM3240G-EVAL 评估板。


 楼主| 我想看大海 发表于 2021-11-9 10:25 | 显示全部楼层
2.2 开始移植
本文以 CubeF4 内的示例代码工程
STM32Cube_FW_F4_V1.10.0\Projects\STM324xG_EVAL\Applications\FreeRTOS\FreeRTOS_ThreadCreation 为例, IDE
使用 IAR。  

第一步: 使用 IAR 打开 FreeRTOS_ThreadCreation 工程, 将 cpu_utils.c 文件添加到工程, 并在工程中添加对应头文件目录:
578366189db9ae98f2.png
第二步: 打开 FreeRTOST 的配置头文件 FreeRTOSConfig.h 修改宏 configUSE_IDLE_HOOK 和 configUSE_TICK_HOOK
的值为 1:
950876189dbab681c0.png
第三步: 继续在 FreeRTOSConfig.h 头文件的末尾处添加 traceTASK_SWITCHED_IN 与 traceTASK_SWITCHED_OUT 定义:
645766189dbb8d3049.png
第四步: 在 main.h 头文件中 include “”cmsis_os.h“”
Main.h :
104846189dbc808c39.png
第五步: 修改工程属性, 使编译过程不需要函数原型:
209996189dbdabfa81.png
第六步:在工程中任何用户代码处都可以调用 osGetCPUUsage()函数来获取当前 CPU 的使用率:
358656189dbf96defd.png
第七步: 编译并运行测试
在调试状态下使用 Live Watch 窗口监控全部变量 osCPU_Usage 的值:
940566189dc0be539b.png
osCPU_Usage 是在 cpu_utils.c 文件中定义的全局变量,表示当前 CPU 的使用率,是个动态值, 由上图可以, CPU 使用率的
动态值为 20%。 实际在代码中是按第六步中调用 osGetCPUUsage()函数来获取当前 CPU 的使用率的。
至此,算法使用方法介绍完毕。


 楼主| 我想看大海 发表于 2021-11-9 10:27 | 显示全部楼层
3 算法实现原理分析
操作系统运行时是不断在不同的任务间进行切换, 而驱动这一调度过程是通过系统 tick 来驱动的,即每产生一次系统 tick 则检
查一下当前正在运行的任务的环境判断是否需要切换任务,即调度, 如果需要,则触发 PendSV,通过在 PendSV 中断调用
vTaskSwitchContext()函数来实现任务的调度。 而本文所要讲述的 CPU 使用率算法是通过在一定时间内( 1000 个时间片内) ,
计算空闲任务所占用的时间片总量, 100 减去空闲任务所占百分比则为工作任务所占百分比,即 CPU 使用率。  

883386189dc3343d0b.png
此函数为空闲任务钩子函数, 每次当切换到空闲任务时就会运行此钩子函数, 它的作用就是记录当前空闲任务的句柄并保存
到全局变量 xIdleHandle。
29176189dc47d7b21.png
此函数为操作系统的 tick 钩子函数,即每次产生系统 tick 中断都会进入到此钩子函数。 此钩子函数实际上就是具体计算 CPU
使用率的算法了。 osCPU_TotalIdleTime 是一个全局变量, 表示在 1000 个 tick 时间内空闲任务总共占用的时间片,
CALCULATION_PERIOD 宏的值为 1000, 即每 1000 个 tick 时间内重新计算一次 CPU 的使用率。
下面两个函数就是如何计算 osCPU_TotalIdleTime 的:
  1. void StartIdleMonitor (void)
  2. {
  3. if( xTaskGetCurrentTaskHandle() == xIdleHandle ) //如果是切入到空闲任务
  4. {
  5. osCPU_IdleStartTime = xTaskGetTickCountFromISR();//记录切入到空闲任务的时间点
  6. }
  7. }
  8. void EndIdleMonitor (void)
  9. {
  10. if( xTaskGetCurrentTaskHandle() == xIdleHandle ) //如果是从空闲任务切出
  11. {
  12. /* Store the handle to the idle task. */
  13. osCPU_IdleSpentTime = xTaskGetTickCountFromISR() - osCPU_IdleStartTime; //计算此次空闲
  14. 任务花费多长时间
  15. osCPU_TotalIdleTime += osCPU_IdleSpentTime; //空闲任务所占时间进行累加
  16. }
  17. }
这两个函数是调度器钩子函数,在调度器进行任务切进和切出时分别回调, StartIdleMonitor()函数记录切换到空闲任务时的时
间点, EndIdleMonitor()则在推出空闲任务时计算此次空闲任务花费多长时间,并累加到 osCPU_TotalIdleTime, 即空闲任务
总共占用的时间片 。
  1. uint16_t osGetCPUUsage (void)
  2. {
  3. return (uint16_t)osCPU_Usage; //直接返回全局变量 osCPU_Usage, 即 CPU 使用率
  4. }
全局变量 osCPU_Usage 保存的就是 CPU 的使用率,它是在操作系统的 tick 钩子函数中每隔 1000 个 tick 就被重新计算一次。


 楼主| 我想看大海 发表于 2021-11-9 10:28 | 显示全部楼层
4 结论
通过此方法可以很好的用来评估 STM23 MCU 的运行性能。
sadicy 发表于 2021-11-11 10:00 | 显示全部楼层
mark
学习了,回头试试。
q1d0mnx 发表于 2021-11-11 12:14 | 显示全部楼层
步骤很详细,研究研究
renzheshengui 发表于 2021-12-6 11:26 | 显示全部楼层
这个非常实用啊
keaibukelian 发表于 2021-12-6 11:28 | 显示全部楼层
考虑的非常周全啊
labasi 发表于 2021-12-6 11:31 | 显示全部楼层
和实际的相符吗
paotangsan 发表于 2021-12-6 11:37 | 显示全部楼层
这个函数是自己写的还是库函数啊
tpgf 发表于 2021-12-6 11:52 | 显示全部楼层
非常简便的方法
wakayi 发表于 2021-12-6 11:55 | 显示全部楼层
如果占用率很大 就跑不起来吧
moticsoft 发表于 2021-12-6 22:12 | 显示全部楼层
有机会试试
xxxt 发表于 2022-3-4 16:19 | 显示全部楼层
AloneKaven 发表于 2022-10-7 20:22 | 显示全部楼层
得到的数据准确吗
Pulitzer 发表于 2022-10-9 08:05 | 显示全部楼层

一种了解状态变化的简单方法
Uriah 发表于 2022-10-9 15:06 | 显示全部楼层

多次检查也会给单片机带来负荷,对功耗不利
Bblythe 发表于 2022-10-9 18:05 | 显示全部楼层

在掌握对象的变化频度时是有效的
SantaBunny 发表于 2023-2-13 16:25 | 显示全部楼层
占用率很大 就跑不起来的吧
您需要登录后才可以回帖 登录 | 注册

本版积分规则

36

主题

282

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部