ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

【计算机基础】-26-RT-Thread-实际系统中,计算CPU使用率的原理与算法,代码示例

【计算机基础】-26-RT-Thread-实际系统中,计算CPU使用率的原理与算法,代码示例

在 RT-Thread 实际系统中,CPU 使用率的计算默认不启用,需通过配置开启。其核心原理是:基于 idle 线程的运行时间占比反推 CPU 负载,但不是简单地在 idle 函数首尾记录 tick(这种 naive 方法会因抢占导致错误),而是采用“线程运行时间统计”机制,在线程切换时精确累加每个线程的实际运行时间。


一、启用条件(rtconfig.h

#define RT_USING_CPU_USAGE // 启用 CPU 使用率功能 #define RT_USING_THREAD_RUNTIME_STATISTICS // 启用线程运行时间统计(关键!)

⚠️ 注意:仅定义RT_USING_CPU_USAGE不够,必须配合RT_USING_THREAD_RUNTIME_STATISTICS才能获得准确结果。


二、核心原理

✅ 正确公式:

CPU 使用率 = (1 - idle_thread 运行时间 / 采样周期总时间) × 100%

  • idle_thread 运行时间:由调度器在线程切换时精确累加(单位:cycles 或 ticks);
  • 采样周期:通常为 1 秒;
  • 时间源:Cortex-M 使用DWT Cycle Counter(高精度),其他平台可用 SysTick。

三、关键数据结构(RT-Thread 源码)

每个线程控制块(TCB)增加运行时间字段:

// rtdef.h struct rt_thread { ... #ifdef RT_USING_THREAD_RUNTIME_STATISTICS rt_uint64_t runtime; // 累计运行时间(cycles) #endif };

四、实际代码实现(以 Cortex-M 为例)

1.初始化 DWT Cycle Counter(board.c

// 在 rt_hw_board_init() 中 void rt_hw_board_init(void) { /* 使能 DWT Cycle Counter */ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // ... 其他初始化 }

2.线程切换时累加运行时间(cpuport.c

RT-Thread 在PendSV 异常处理上下文切换汇编中调用以下函数:

// libcpu/arm/cortex-m/cpuport.c #include <rthw.h> #ifdef RT_USING_THREAD_RUNTIME_STATISTICS static rt_uint32_t last_cyc = 0; void rt_update_runtime(rt_ubase_t *sp) { rt_uint32_t cyc_now = DWT_CYCCNT; rt_thread_t thread = rt_current_thread; if (last_cyc == 0) { last_cyc = cyc_now; return; } if (thread) { /* 累加当前线程运行时间 */ thread->runtime += (cyc_now - last_cyc); } last_cyc = cyc_now; } #endif

🔔 注:实际调用位置在context_gcc.S的 PendSV_Handler 中:

#ifdef RT_USING_THREAD_RUNTIME_STATISTICS bl rt_update_runtime #endif

3.CPU 使用率计算(cpuusage.c

// src/cpuusage.c #include <rtthread.h> static rt_uint64_t last_total_time = 0; static rt_uint64_t last_idle_time = 0; static rt_uint8_t cpu_usage = 0; rt_uint8_t rt_cpu_get_usage(void) { return cpu_usage; } void rt_cpu_usage_calc(void) { rt_thread_t idle_thread; rt_uint64_t total_time, idle_time; rt_uint64_t delta_total, delta_idle; /* 获取 idle 线程(单核系统默认名为 "tidle0")*/ idle_thread = rt_thread_find("tidle0"); if (!idle_thread) return; /* 总时间 = DWT_CYCCNT(或所有线程 runtime 之和)*/ total_time = DWT_CYCCNT; idle_time = idle_thread->runtime; /* 计算差值 */ delta_total = total_time - last_total_time; delta_idle = idle_time - last_idle_time; if (delta_total > 0) { /* CPU 使用率 = (1 - idle/total) * 100 */ cpu_usage = (rt_uint8_t)(100 - (delta_idle * 100 / delta_total)); } last_total_time = total_time; last_idle_time = idle_time; }

4.定期触发计算(通过 idle hook)

// 应用代码 static rt_tick_t last_calc_tick = 0; void cpu_usage_hook(void) { /* 每秒计算一次 */ if (rt_tick_get() - last_calc_tick >= RT_TICK_PER_SECOND) { rt_cpu_usage_calc(); last_calc_tick = rt_tick_get(); } } int cpu_usage_init(void) { rt_thread_idle_sethook(cpu_usage_hook); return 0; } INIT_APP_EXPORT(cpu_usage_init);

五、用户使用示例

1.FinSH 命令

msh /> cpu_usage CPU usage: 12%

2.应用代码获取

#include <rtthread.h> void cpu_monitor_thread(void *param) { while (1) { rt_kprintf("CPU Usage: %d%%\n", rt_cpu_get_usage()); rt_thread_mdelay(1000); } } /* 创建监控线程 */ rt_thread_t tid = rt_thread_create("cpu_mon", cpu_monitor_thread, RT_NULL, 512, 10, 10); rt_thread_startup(tid);

六、为什么不会受抢占影响?

关键设计

  • 运行时间在线程切换时刻(PendSV)累加;
  • 每次只累加上一个线程从上次切换到本次切换的真实运行时间
  • idle 线程的runtime仅包含它真正占用 CPU 的时间
  • 中断或其他线程的运行时间会计入它们自己的runtime不会污染 idle 统计

📌 示例:

  • idle 运行 100 cycles → 被 UART 中断打断;
  • ISR 运行 50 cycles → 切换到线程 A 运行 200 cycles;
  • 最终:
    • idle.runtime += 100
    • threadA.runtime += 200
    • ISR 时间通常计入被中断的线程(或单独统计)
      idle 时间统计完全准确

七、注意事项

项目说明
硬件依赖DWT 仅支持 Cortex-M3/M4/M7/M33,Cortex-M0 需改用 SysTick(精度较低)
多核系统需分别统计每个核的 idle 线程(如 "tidle0", "tidle1")
idle 线程名单核默认为"tidle0",可通过list_thread查看
开销极小(每次切换仅 1 次减法 + 1 次加法)

✅ 总结

RT-Thread 实际系统中 CPU 使用率计算:

  1. 原理CPU Usage = 1 - (idle_thread 运行时间 / 总时间)
  2. 机制:在线程切换时,用DWT Cycle Counter精确累加每个线程的运行时间;
  3. 抗干扰:完全不受中断、抢占影响;
  4. 启用:需定义RT_USING_CPU_USAGE+RT_USING_THREAD_RUNTIME_STATISTICS
  5. 输出:通过rt_cpu_get_usage()或 FinSH 命令获取。

🔑核心思想
“通过调度器打时间戳,精确记录每个线程的真实运行时间,从而可靠计算 CPU 负载。”

返回列表