在嵌入式实时系统中,CPU使用率是衡量系统运行状态的重要指标。

对于8051这类资源受限的单片机而言,CPU资源更加有限。当系统同时运行多个任务、定时器、中断以及通信服务时,仅仅确认“系统能够正常运行”是不够的,还需要进一步了解:

  • 当前CPU到底有多忙?

  • 系统还有多少空闲处理能力?

  • 是否存在任务长期占用CPU?

  • 修改任务周期或增加功能后,CPU负载是否明显上升?

针对这些问题,HRTOS Debug提供了CPU Usage功能,用于统计HRTOS当前CPU使用率。

本文介绍该功能的基本原理、核心实现以及实际使用方法。


一、CPU使用率的基本思路

HRTOS Debug并没有通过复杂的硬件性能计数器统计CPU时间,而是利用RTOS中的 Idle Task(空闲任务) 完成CPU使用率估算。

系统运行过程中:

CPU
│
├── 用户任务
├── 系统任务
├── 中断处理
└── Idle Task

当其他任务和中断都没有工作时,CPU会进入Idle Task。

因此:

CPU越忙,Idle Task获得的运行时间越少;CPU越空闲,Idle Task获得的运行时间越多。

于是可以通过统计一段时间内Idle Task的活动量,反推出CPU空闲比例:

CPU使用率 = 100% - CPU空闲率

这是嵌入式RTOS中比较常见的一种CPU负载统计思路,同时不需要额外占用硬件定时器资源。


二、HRTOS Debug的实现结构

CPU Usage主要由以下几个部分组成:

HRTOS_DEBUG_CPU_Init()
        │
        ▼
   初始化统计变量
        │
        ▼
HRTOS_DEBUG_CPU_IdleHook()
        │
        ▼
   Idle Task持续统计
        │
        ▼
HRTOS_DEBUG_CPU_Sample()
        │
        ▼
    计算CPU使用率
        │
        ▼
HRTOS_DEBUG_CPU_GetUsage()

其中最核心的是Idle Hook。

系统每次运行Idle Task时,通过Idle Hook记录Idle活动量。


三、Idle计数器

首先定义一个Idle累计计数器:

static volatile unsigned long g_cpu_idle_counter = 0;

它用于记录统计过程中Idle Task的有效活动次数。

同时记录上一次检测到的Tick值:

static unsigned int g_cpu_last_tick_low = 0;

HRTOS内部维护系统Tick计数,因此Idle Hook可以通过Tick变化判断当前是否进入了新的系统Tick周期。

核心代码如下:

void HRTOS_DEBUG_CPU_IdleHook(void)
{
    unsigned int tick_low;

    tick_low = OS_TICK_COUNT_L;

    if (tick_low != g_cpu_last_tick_low)
    {
        g_cpu_idle_counter++;

        g_cpu_last_tick_low = tick_low;
    }
}

这里并不是简单地“Idle Task运行一次就加1”。

而是检测:

OS_TICK_COUNT_L
        │
        ├── 没有变化 → 不统计
        │
        └── 发生变化 → Idle活动计数 +1

这样可以避免Idle Task在同一个Tick周期内高速循环时,使统计结果受到循环次数影响。


四、为什么使用Idle Task统计CPU

这种设计对于8051尤其合适。

如果直接通过某个硬件定时器统计CPU运行时间,需要考虑:

  • 定时器资源占用

  • 中断开销

  • 与系统Tick的协调

  • 不同任务之间的时间统计

  • 8051有限的硬件资源

而HRTOS本身已经存在Idle Task。

因此可以直接利用:

系统有工作
    ↓
其他任务 / 中断运行
    ↓
Idle Task获得的时间减少

系统没有工作
    ↓
Idle Task持续运行
    ↓
Idle活动增加

最终通过Idle活动量估算CPU负载。

这也是HRTOS Debug作为一个独立调试组件的一个设计思路:

尽量复用HRTOS已有机制,而不是为了Debug功能引入额外的系统负担。


五、CPU使用率计算

HRTOS内部维护一个32位Tick计数:

extern unsigned int xdata OS_TICK_COUNT_L;
extern unsigned int xdata OS_TICK_COUNT_H;

在采样函数中,将高16位和低16位组合成完整的Tick计数:

tick_count = OS_TICK_COUNT_H;
tick_count <<= 16;
tick_count += OS_TICK_COUNT_L;

得到:

Tick Count =
    High 16 bit
    +
    Low 16 bit

随后以当前Tick数量作为统计基准。

CPU使用率计算公式为:

CPU Usage
    =
100 - Idle Percentage

代码实现:

g_cpu_usage =
    (unsigned char)
    (
        100 -
        ((g_cpu_idle_counter * 100UL) / g_cpu_idle_max)
    );

因此整体关系可以表示为:

              Idle Activity
Idle % = -------------------------
              Statistics Base

CPU % = 100% - Idle %

例如在一个统计周期内,Idle活动量约占统计基准的20%,那么:

Idle ≈ 20%

CPU ≈ 100% - 20%
    ≈ 80%

六、CPU使用率采样

HRTOS Debug提供:

void HRTOS_DEBUG_CPU_Sample(void)

用于执行一次CPU使用率采样。

建议根据实际应用选择合适的统计周期,例如:

100 ms ~ 1 s

对于任务变化较快的系统,可以使用较短周期。

对于希望观察整体系统负载的场景,可以使用较长周期。

需要注意的是,CPU Usage属于统计量,并不是某一个瞬间CPU执行状态的绝对测量值。

因此:

统计周期越长,结果通常越适合观察系统整体负载;统计周期越短,结果对瞬时变化更加敏感。


七、获取CPU使用率

应用程序可以直接调用:

unsigned char HRTOS_DEBUG_CPU_GetUsage(void)

获取当前CPU使用率。

例如:

unsigned char cpu;

cpu = HRTOS_DEBUG_CPU_GetUsage();

返回值范围:

0 ~ 100

可以进一步配合Shell、串口显示或者其他Debug界面输出:

CPU Usage : 72%

这样开发人员无需修改任务代码,就可以直接观察系统当前负载。


八、初始化与使用流程

HRTOS Debug CPU功能的使用流程比较简单。

1. 系统初始化

在系统初始化阶段调用:

HRTOS_DEBUG_CPU_Init();

初始化内部统计变量。

2. 注册Idle Hook

将:

HRTOS_DEBUG_CPU_IdleHook()

注册到HRTOS的Idle Hook中。

Idle Task运行过程中会自动执行CPU统计。

3. 周期采样

根据应用需求周期调用:

HRTOS_DEBUG_CPU_Sample();

更新CPU使用率。

4. 获取结果

应用程序通过:

HRTOS_DEBUG_CPU_GetUsage();

获取最终CPU使用率。

整体流程:

系统启动
   │
   ▼
CPU Debug初始化
   │
   ▼
Idle Task运行
   │
   ├── Idle Hook
   │      │
   │      └── 累计Idle活动
   │
   ▼
周期性Sample
   │
   ▼
计算Idle比例
   │
   ▼
计算CPU Usage
   │
   ▼
Debug / Shell / 用户程序读取

九、这个功能的实际价值

CPU使用率并不是一个单纯用于“显示百分比”的功能。

在实际开发过程中,它可以帮助定位很多问题。

例如一个系统原本:

CPU Usage : 35%

增加通信任务以后:

CPU Usage : 52%

进一步增加传感器处理:

CPU Usage : 71%

开发人员就能够直观看到系统负载变化。

如果某次修改以后CPU使用率突然从:

40%

上升到:

90%

那么就需要进一步检查:

  • 是否存在高频任务

  • 是否存在过短的任务周期

  • 是否有任务长时间运行

  • 是否存在不合理的轮询

  • 是否有异常中断

  • 是否需要调整任务执行方式

因此CPU Usage实际上可以作为 系统性能分析的入口


十、与任务调试结合

单独知道CPU使用率还不够。

例如:

CPU Usage : 82%

这个数字只能说明系统比较繁忙,却不能直接告诉开发人员:

“到底是谁占用了CPU?”

这也是HRTOS Debug进一步提供任务监控能力的意义。

可以形成:

CPU Usage
    │
    ▼
发现CPU负载较高
    │
    ▼
Task Monitor
    │
    ▼
查看任务状态
    │
    ▼
分析任务周期 / 阻塞 / 运行情况

进一步结合任务栈使用率等信息,就可以形成更加完整的运行状态分析。

因此,CPU Usage并不是孤立的一个小功能,而是HRTOS Debug整体调试体系中的一个基础模块。


十一、8051平台下的设计考虑

8051与32位MCU相比,资源非常有限。

因此在设计Debug功能时,需要考虑Debug本身不能成为系统负担。

CPU Usage模块主要使用:

unsigned long
unsigned int
unsigned char

等基础数据类型完成统计,并直接复用HRTOS已有的系统Tick和Idle Task机制。

这种设计符合HRTOS的一贯思路:

在有限的8051资源条件下,尽可能提供完整的系统级能力。

Debug不是简单地增加一个“CPU百分比显示”,而是在不改变HRTOS核心调度机制的情况下,为开发人员增加系统运行状态的可观测能力。


十二、完整核心代码

CPU Usage模块核心实现如下:

#include "HRTOS_Debug.h"

extern unsigned int xdata OS_TICK_COUNT_L;
extern unsigned int xdata OS_TICK_COUNT_H;

static volatile unsigned long g_cpu_idle_counter = 0;
static unsigned int g_cpu_last_tick_low = 0;
static unsigned long g_cpu_idle_max = 1;
static unsigned char g_cpu_usage = 0;

void HRTOS_DEBUG_CPU_Init(void)
{
    g_cpu_idle_counter = 0;
    g_cpu_last_tick_low = 0;

    g_cpu_idle_max = 1;
    g_cpu_usage = 0;
}

void HRTOS_DEBUG_CPU_IdleHook(void)
{
    unsigned int tick_low;

    tick_low = OS_TICK_COUNT_L;

    if (tick_low != g_cpu_last_tick_low)
    {
        g_cpu_idle_counter++;
        g_cpu_last_tick_low = tick_low;
    }
}

void HRTOS_DEBUG_CPU_Sample(void)
{
    unsigned long tick_count;

    tick_count = OS_TICK_COUNT_H;
    tick_count <<= 16;
    tick_count += OS_TICK_COUNT_L;

    if (tick_count == 0)
    {
        g_cpu_usage = 0;
        return;
    }

    g_cpu_idle_max = tick_count;

    g_cpu_usage =
        (unsigned char)
        (
            100 -
            ((g_cpu_idle_counter * 100UL) / g_cpu_idle_max)
        );

    if (g_cpu_usage > 100)
    {
        g_cpu_usage = 100;
    }
}

unsigned char HRTOS_DEBUG_CPU_GetUsage(void)
{
    HRTOS_DEBUG_CPU_Sample();

    return g_cpu_usage;
}

十三、总结

HRTOS Debug CPU Usage采用 Idle Task + 系统Tick统计 的方式,对8051系统CPU负载进行估算。

整个机制可以概括为:

Idle Task
    ↓
统计Idle活动
    ↓
获得CPU空闲比例
    ↓
100% - Idle%
    ↓
CPU Usage

它具有几个特点:

  • 不需要额外占用硬件定时器

  • 直接利用HRTOS现有Idle Task机制

  • 实现简单,适合8051资源环境

  • 可以与任务监控、栈监控等Debug功能结合

  • 能够辅助分析系统负载变化

  • 为嵌入式系统性能优化提供直观数据

对于一个实时操作系统而言,“能够运行”只是第一步,“能够观察系统为什么这样运行”同样重要。

HRTOS Debug的意义,就在于为HRTOS增加这层系统运行状态的可观测能力。

从CPU使用率开始,再结合任务、栈、阻塞状态等信息,可以逐步建立起一套面向8051实时系统的调试与分析工具链。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐