你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】一种计算 CPU 使用率的方法及其实现原理

[复制链接]
STMCU小助手 发布时间:2022-3-5 10:00
1 前言
. c7 U) m/ I/ U" b* O" l5 L% K出于性能方面的考虑,有的时候,我们希望知道 CPU 的使用率为多少,进而判断此 CPU 的负载情况和对于当前运行环境是否足够“胜任”。本文将介绍一种计算 CPU 占有率的方法以及其实现原理。5 D5 s& O5 C9 l

# {/ t* z4 T& v! ^" s* c2 移植算法
  _2 W) V7 C4 A1 X8 ^5 S6 h2.1 算法简介
: A1 I2 m+ p9 K: E
此算法是基于操作系统的,理论上不限于任何操作系统,只要有任务调度就可以。本文将以 FreeRTOST 为例来介绍本算法的使用方法。
& j9 S+ w4 s6 }/ ]9 X, \本文所介绍的算法出处为随 Cube 库一起提供的,它在 cube 库中的位置如下图所示:
# f. m3 G( R3 c2 E
& c0 _, P; H  o. I TA`{%P{P33M{K43(760U)70.png " q* r9 S% L8 s1 |9 R/ w4 D0 n- B4 M" C
7 m1 D- m/ c8 `' G. Y5 G$ c
本文将以 STM32F4 为例,测试环境为 STM3240G-EVAL 评估板。6 D# w/ F3 b/ ~* h+ }5 H
; C) ?% P8 b; C( q5 C' F& u- q- o
2.2 开始移植" }5 f6 F6 e4 l7 }
本文以 CubeF4 内的示例代码工程STM32Cube_FW_F4_V1.10.0\Projects\STM324xG_EVAL\Applications\FreeRTOS\FreeRTOS_ThreadCreation 为例,IDE使用 IAR。
% q9 Y6 H' R% f& _5 G第一步:使用 IAR 打开 FreeRTOS_ThreadCreation 工程,将 cpu_utils.c 文件添加到工程,并在工程中添加对应头文件目录:
" T& c1 c& I* A  C2 g- i( }
" `& G1 W* M0 s0 ~, [' \7 F 8CTMGLB)ZFZ[%[~8%INEM}N.png
8 S# z2 C0 x, Q: H5 Q
( m6 [/ n9 B; m0 r: y6 O0 m. O7 V- J第二步:打开 FreeRTOST 的配置头文件 FreeRTOSConfig.h 修改宏 configUSE_IDLE_HOOK 和 configUSE_TICK_HOOK的值为 1:
# a  w) p  {' l
  1. #define configUSE_PREEMPTION 1( s* Y( O8 A2 C$ r2 n
  2. #define configUSE_IDLE_HOOK 1 //修改此宏的值为 1/ g# c. [- x( \8 [/ R% i% A
  3. #define configUSE_TICK_HOOK 1 //修改此宏的值为 1
    # `. F9 T- x  z! H  P6 u8 H9 g/ q3 ~
  4. #define configCPU_CLOCK_HZ ( SystemCoreClock )
    5 E9 Y' o4 a# e5 _
  5. #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
    ( l8 E# f8 R- g$ g
  6. #define configMAX_PRIORITIES ( 8 )
    ' E! d5 e% @1 g! T
  7. #define configMINIMAL_STACK_SIZE ( ( uint16_t ) 128 )
复制代码

! A0 n0 y) h3 z" u7 f+ Z第三步:继续在 FreeRTOSConfig.h 头文件的末尾处添加 traceTASK_SWITCHED_IN 与 traceTASK_SWITCHED_OUT 定义:
3 \3 J- I- `6 A
  1. #define traceTASK_SWITCHED_IN() extern void StartIdleMonitor(void); ' u. M+ P$ n5 U+ m2 A& t
  2. StartIdleMonitor()1 M9 o$ e- w6 f2 G
  3. #define traceTASK_SWITCHED_OUT() extern void EndIdleMonitor(void);
    7 [7 k. d6 r& ?3 B6 n9 `0 @
  4. EndIdleMonitor()
复制代码
2 X5 r: x: y: C: y6 S# O# y' A
第四步:在 main.h 头文件中 include “”cmsis_os.h“”% i  D1 z" _" X9 |( Z. ?
Main.h :
4 {. ^. l  V& @' N+ U: t5 r, H
  1. #include "stm32f4xx_hal.h"7 j$ m5 Z- l/ b7 f! Q
  2. #include "stm324xg_eval.h"
    ; e0 X: M# c! {; `8 r
  3. #include "cmsis_os.h" //添加包含此头文件
    3 J/ {2 r4 U; w+ k8 a; R9 `  s4 b
  4. //…
复制代码

5 p" h9 _9 F, M/ k第五步: 修改工程属性,使编译过程不需要函数原型:/ o& A6 ^6 M  D; Y8 @4 N

5 B$ Q: y! i: H; u( w" i7 ^8 l/ F ZEKC[Q]QPCG~3`B[U4FHS5P.png 6 q7 V3 u2 q$ C" D0 R1 C

! \" {9 R7 x+ ^6 }! I- V( Z  F4 D) l第六步:在工程中任何用户代码处都可以调用 osGetCPUUsage()函数来获取当前 CPU 的使用率:
. f. d, p1 o( v2 d9 |/ H
  1. static void LED_Thread2(void const *argument)6 c7 t1 V( f. K4 @; D! c+ [
  2. {7 T5 n5 s' K2 J" F7 |& k
  3. uint32_t count;! M5 S5 j+ m- f: a+ e
  4. uint16_t usage =0;" S% N( p" }( k0 }" M0 Q- n6 Y
  5. (void) argument;
    9 c2 {6 h) G9 X* q2 |& ]* U  J2 {

  6. $ o' y/ U5 V$ \
  7. for(;;)- q, |" f1 |2 X5 _
  8. {
    . A' \9 T% W. S( D
  9. count = osKernelSysTick() + 10000;+ V, V. Q. |3 f% R4 h
  10. ' f8 ]8 L$ Y3 j$ c
  11. /* Toggle LED2 every 500 ms for 10 s */9 q! l; c4 A4 A/ e5 w) P
  12. while (count >= osKernelSysTick())
    $ ?9 x3 w: x! q3 N+ p. ^( W
  13. {
    7 a4 F! V/ Y3 I! |' D2 C
  14. BSP_LED_Toggle(LED2);
    0 ~3 u1 n$ A5 y3 ?$ Y, _
  15. 0 ~5 v+ p1 B% m2 ?1 I) ~; o
  16. osDelay(500);
    8 s( b" q: G9 ^- n5 }
  17. }0 |0 M0 c( u2 i( O- i
  18. usage =osGetCPUUsage(); //获取当前 CPU 的使用率7 l8 [3 o; C5 W1 `$ V
  19. /* Turn off LED2 */
    - ^4 ^  `8 S+ U& C3 Y" r
  20. BSP_LED_Off(LED2);) W3 t$ l- P4 G! y

  21. 3 ?5 r( n8 a8 N2 \, m9 d7 f, i
  22. /* Resume Thread 1 */% Y# _" j1 n) @! Q* l& s
  23. osThreadResume(LEDThread1Handle);8 o! a9 D/ t3 b" L

  24. 0 B4 E/ V% {' U6 n- B1 O
  25. /* Suspend Thread 2 */
    & ~7 j- R& T7 D4 R& Y, {- M1 [
  26. osThreadSuspend(NULL);6 e: i- p, q) G9 @+ q7 u
  27. }* a1 _; x- t) u
  28. }
复制代码
8 {" J4 g% }/ Z( N- u* K
第七步:编译并运行测试- J) Y# d/ I! T  L
在调试状态下使用 Live Watch 窗口监控全部变量 osCPU_Usage 的值:7 O/ u. t# E! T" b. `( B9 b

4 z' \$ i, b' Z3 d0 n+ E# [7 p 6DN{URB]Y3CT1UNS13]R{9B.png % S* E* l3 h/ [' a# D

: T2 l: H+ H' }; E+ W( [osCPU_Usage 是在 cpu_utils.c 文件中定义的全局变量,表示当前 CPU 的使用率,是个动态值,由上图可以,CPU 使用率的动态值为 20%。实际在代码中是按第六步中调用 osGetCPUUsage()函数来获取当前 CPU 的使用率的。
4 t7 n0 F. V8 r' t( r/ ^至此,算法使用方法介绍完毕。
- B# H" r0 g4 v" d! F& [! V1 ~! F1 ^$ c1 H
3 算法实现原理分析! W+ p2 t% A( l6 `
操作系统运行时是不断在不同的任务间进行切换,而驱动这一调度过程是通过系统 tick 来驱动的,即每产生一次系统 tick 则检查一下当前正在运行的任务的环境判断是否需要切换任务,即调度,如果需要,则触发 PendSV,通过在 PendSV 中断调用vTaskSwitchContext()函数来实现任务的调度。而本文所要讲述的 CPU 使用率算法是通过在一定时间内(1000 个时间片内),计算空闲任务所占用的时间片总量,100 减去空闲任务所占百分比则为工作任务所占百分比,即 CPU 使用率。- n; D, p  |& W
  1. void vApplicationIdleHook(void). ?0 k1 \, \/ T3 J, f- J& k
  2. {; E6 ]/ b! }* v: r1 r% I
  3. if( xIdleHandle == NULL )
    : f0 g, Z$ g  _; @! i! `# Q- c
  4. {
    . I# ~& \( h9 c* g: F* @5 f" ^
  5. /* Store the handle to the idle task. */
    8 |! O4 k9 N7 |
  6. xIdleHandle = xTaskGetCurrentTaskHandle(); //记录空闲任务的句柄, H4 e/ R+ [  A0 d
  7. }
    7 Z/ X& f+ |& E2 M7 W6 I2 l' _" {
  8. }
复制代码

; E- Q9 i+ |% k; k此函数为空闲任务钩子函数,每次当切换到空闲任务时就会运行此钩子函数,它的作用就是记录当前空闲任务的句柄并保存到全局变量 xIdleHandle。0 E  z; L% \5 T- u( R8 ~8 x
  1. void vApplicationTickHook (void)
    5 Y1 {! r! X5 p/ n/ F. z
  2. {
    - C& D, m  c0 I2 f; v
  3. static int tick = 0;5 |2 h; M6 {! w+ r: c3 v9 M
  4. : |* V% a4 m9 \
  5. if(tick ++ > CALCULATION_PERIOD) //每 1000 个 tick,刷新一次 CPU 使用率
    % X" G9 t0 ~. @3 h3 ^
  6. {3 p0 m3 Y' C  P4 _
  7. tick = 0;) v4 a, Y5 c% C  v
  8. 4 b2 A' x3 N6 a
  9. if(osCPU_TotalIdleTime > 1000)% Y' U9 n: x) l8 C; T0 N! |
  10. {4 k  s/ c) A: ]4 |2 l# P
  11. osCPU_TotalIdleTime = 1000;2 j; U6 j8 N' |: Z0 _9 X
  12. }( ~4 w% B) K4 x7 l- ?. z$ o
  13. osCPU_Usage = (100 - (osCPU_TotalIdleTime * 100) / CALCULATION_PERIOD); //这行代码就是
    9 l' x, @* U7 d3 G, m( n
  14. CPU 使用率的具体计算方法了7 g8 N% g7 T; ^8 V. H* Z
  15. osCPU_TotalIdleTime = 0;
    ) Z4 D4 ?( x5 t, x, e+ a- i" n
  16. }
    # j9 D2 n" V$ b
  17. }
复制代码
* @- V  S8 F& Y
此函数为操作系统的 tick 钩子函数,即每次产生系统 tick 中断都会进入到此钩子函数。此钩子函数实际上就是具体计算 CPU使用率的算法了。osCPU_TotalIdleTime 是一个全局变量,表示在 1000 个 tick 时间内空闲任务总共占用的时间片,CALCULATION_PERIOD 宏的值为 1000,即每 1000 个 tick 时间内重新计算一次 CPU 的使用率。: Q. Q2 s6 O' |& l" w9 A! A( s' M
下面两个函数就是如何计算 osCPU_TotalIdleTime 的:& C# [- J9 A! H5 X2 P+ n! z
  1. void StartIdleMonitor (void)
    % Y1 ~2 k. n1 K0 N
  2. {
    ) U1 c. X% l+ c5 e( v
  3. if( xTaskGetCurrentTaskHandle() == xIdleHandle ) //如果是切入到空闲任务( s0 t$ r" @% E" i9 Q% a4 i
  4. {
    8 P/ q* Q+ w/ `5 X+ }1 y
  5. osCPU_IdleStartTime = xTaskGetTickCountFromISR();//记录切入到空闲任务的时间点& O/ Q6 t8 K- Y2 L# e
  6. }
    5 ?' W/ P, Z6 [5 M9 S# X% e) l3 Q' |
  7. }
    7 Y0 D. Z) O9 r% J
  8. void EndIdleMonitor (void)! B+ s2 r% H6 Y
  9. {
    0 o( [2 {% V0 [* p1 i) o
  10. if( xTaskGetCurrentTaskHandle() == xIdleHandle ) //如果是从空闲任务切出% f& @4 S1 l; d) o8 B3 n6 {
  11. {6 e; w7 Y6 b3 v! u
  12. /* Store the handle to the idle task. */
    . i" U$ _8 h! V" ^# `
  13. osCPU_IdleSpentTime = xTaskGetTickCountFromISR() - osCPU_IdleStartTime; //计算此次空闲
    ' K9 Y. x! A3 _
  14. 任务花费多长时间
    5 t' a  o! }1 H* ~7 w$ |' v
  15. osCPU_TotalIdleTime += osCPU_IdleSpentTime; //空闲任务所占时间进行累加( _2 w! y% d& J. x
  16. }
    - F# P  z# F& b- e
  17. }
复制代码

- X! o8 ?. T7 c/ _* `$ n这两个函数是调度器钩子函数,在调度器进行任务切进和切出时分别回调,StartIdleMonitor()函数记录切换到空闲任务时的时间点,EndIdleMonitor()则在推出空闲任务时计算此次空闲任务花费多长时间,并累加到 osCPU_TotalIdleTime,即空闲任务总共占用的时间片。1 v' k8 |. M7 G3 l2 X. D
  1. uint16_t osGetCPUUsage (void)
    5 U5 n/ ], u5 H7 _, e5 c
  2. {
    ; y+ i: q0 r; T) o) z0 s
  3. return (uint16_t)osCPU_Usage; //直接返回全局变量 osCPU_Usage,即 CPU 使用率
    5 T: P2 C1 p3 C& w# v
  4. }
复制代码
1 h# z0 c4 n; i5 z
全局变量 osCPU_Usage 保存的就是 CPU 的使用率,它是在操作系统的 tick 钩子函数中每隔 1000 个 tick 就被重新计算一次。
& l: |0 \. M1 k- g. ?8 V
# _9 F* y( S1 [" T

6 E) {# p! o. a# Q+ g4 结论8 J8 y3 T$ s. Y
通过此方法可以很好的用来评估 STM23 MCU 的运行性能。9 k2 V" m& p1 Z

7 d: @6 M  i, N/ z" y0 i
赞 收藏 评论0 发布时间:2022-3-5 10:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版