基于裸机的cpu loading监控方案

基于裸机的cpu loading监控方案

工程代码:https://github.com/i-jaffer/cpu_monitor_no_thread

1. What's this​!​ ​​

cpu_monitor_no_thread 是一个针对裸机应用编写的 cpu loading 监控方案。使用 FreeRTOS、RT-Thread 这类操作系统时,往往 RTOS 内都自带了 cpu monitor 的组件,但是针对裸机应用场景,却缺乏此类组件,因此在进行逻辑开发时,对于 CPU loading 统计便成了问题,因此开发了 cpu_monitor_no_thread 此组件。

使用 cpu_monitor_no_thread 时,建议对应的裸机开发也需遵循一定的任务思想进行开发,这样统计 cpu loading 才具有一定的意义,如果将事情全部放在while(1) 循环内,不关心执行频率,cpu 能跑多少次就跑多少次,对于这类应用场景,相信你也无须考虑 cpu loading 的问题,因为你的 cpu 一直都在忙着干活,一直都是100%

2. Other

为避免使用此组件的开发者未能理解裸机任务开发思想,特作此补充说明,若已理解可直接跳过

任务开发思想:即将 cpu 需要做的事情拆分为多个任务,如 A、B、C,并根据需求确定相关任务的执行频率,如:

  • 任务A:10ms 执行一次
  • 任务B:20ms 执行一次
  • 任务C:30ms 执行一次

由系统定时器(Cortex M处理器通常使用 system tick)产生基础时间节拍 tick,如 1ms 产生一个 tick

下方展示一个简单的由 system tick 产生时间节拍的处理方式:

c 复制代码
uint32_t clock_array[5]={0};		/* 用于拆分任务处理 */
uint32_t system_tick = 0;

void SysTick_Handler(void)
{
	uint8_t i = 0;
	system_tick ++;
	for(i = 0; i < 5; i++)
	{
		if(clock_array[i] != 0)
			clock_array[i] --;
	}
}

有了 system tick 提供的时间片之后,之后在主循环内即可设计对应任务处理了,伪代码如下:

c 复制代码
int main(void)
{
    /* xxxx init */
    while (1) {
        if (clock_array[0] == 0) {
            clock_array[0] = 10;
            A();	/* 执行A任务*/
        }
        if (clock_array[1] == 0) {
            clock_array[1] = 20;
            B();	/* 执行A任务*/
        }
        if (clock_array[2] == 0) {
            clock_array[2] = 30;
            C();	/* 执行A任务*/
        }
    }
}

3. Usage

3.1 Steps

step1:

添加 cpu_monitor_no_thread 组件至对应工程

step2:

根据对应平台,适配系统时间戳接口,uint32_t cpu_monitor_get_systick(void),接口声明位于cpu_monitor_interface.h 文件内

step3:

系统时钟初始化之后,且其他外设/中断未启动前,调用 cpu_monitor_init(); ,务必注意在系统时钟初始化之后再调用,否则初始化将失败;

此函数运行固定时间用来统计全cpu计算的基(分母),因此要求在其他外设/中断未启用前调用的,这样更加精准

step4:

主循环内调用 cpu_monitor_run_hook() 统计cpu空闲时占比

step5:

系统时钟中断内调用 cpu_monitor_calculate() 定周期计算 cpu loading

可通过修改 #define CPU_MONITOR_PERIOD_TICK 100 ,调整 cpu loading 计算周期

step6:

可调用 cpu_monitor_get_loading() 获取当前 cpu 使用率

3.2 Example

系统滴答处理:

复制代码
uint32_t clock_array[5]={0};		/* 用于拆分任务处理 */
uint32_t system_tick = 0;

void SysTick_Handler(void)
{
	uint8_t i = 0;
	system_tick ++;
	for(i = 0; i < 5; i++)
	{
		if(clock_array[i] != 0)
			clock_array[i] --;
	}
	cpu_monitor_calculate();
}

主函数处理:

c 复制代码
#include "cpu_monitor.h"
#include "cpu_monitor_interface.h"

uint32_t cpu_monitor_get_systick(void)
{
    return get_systick();
}

int main(void)
{
	uint8_t major = 0, minor = 0;
	Systick_Init();
	cpu_monitor_init();
    
    /* xxxx init */
    while (1) {
        cpu_monitor_run_hook();
        
        if (clock_array[0] == 0) {
            clock_array[0] = 10;
            A();	/* 执行A任务*/
        }
        if (clock_array[1] == 0) {
            clock_array[1] = 20;
            B();	/* 执行A任务*/
        }
        if (clock_array[2] == 0) {
            clock_array[2] = 30;
            C();	/* 执行A任务*/
        }
        if (clock_array[3] == 0) {
            clock_array[3] = 1000;
            cpu_monitor_get_loading(&major, &minor);
            printf("cpu loading:%d.%02d\r\n", major, minor);
        }
    }
}

4. Record

实现 cpu monitor 没什么太大困难,但是有一个坑点需要特别注意:

4.1 注意init和hook实现count计数逻辑一致性

实现 cpu_monitor_init() 的时候,主要思想是让cpu不干别的,只做 count 计算,统计cpu全力计数时能计到的最大值,作为后续cpu loading计算时的分母。那么这里务必注意代码编写的处理逻辑一定要和 cpu_monitor_run_hook() 里面空闲cpu计数的处理方式一致!

比如, cpu_monitor_run_hook() 运用的是 if 处理加 自加 运算,对应 cpu_monitor_init()里面也应如此!

c 复制代码
void cpu_monitor_init(void)
{
    uint32_t tick = cpu_monitor_get_systick();
    while (cpu_monitor_get_systick() - tick < CPU_MONITOR_PERIOD_TICK) {
        cpu_monitor.data.total_loop++;
        if (cpu_monitor.data.total_loop >= CPU_MONITOR_MAX_LOOP) {
            cpu_monitor.data.total_loop = 0;
            cpu_monitor.data.total_count ++;
        }
    }
    cpu_monitor.status.init_flag = 1;
}

void cpu_monitor_run_hook(void)
{
    cpu_monitor.data.run_loop++;
    if (cpu_monitor.data.run_loop >= CPU_MONITOR_MAX_LOOP) {
        cpu_monitor.data.run_loop = 0;
        cpu_monitor.data.run_count ++;
    }
}

如果 cpu_monitor_init() 里面换另外一种方式,如 while

c 复制代码
void cpu_monitor_init(void)
{
    uint32_t tick = cpu_monitor_get_systick();
    while (cpu_monitor_get_systick() - tick < CPU_MONITOR_PERIOD_TICK) {
        cpu_monitor.data.total_count++;
        cpu_monitor.data.total_loop = 0;
        while (cpu_monitor.data.total_loop < CPU_MONITOR_MAX_LOOP)
           cpu_monitor.data.total_loop++;
    }
    cpu_monitor.status.init_flag = 1;
}

那么你会发现,代码里面不放什么一开始的 loading 就是40%了!这是因为两种写法不一致,导致 cpu 执行周期不一致,进而导致的统计误差!

4.2 注意while循环内任务思想开发

这个不多说,章节2有详细描述!

5. Conclusion

🆗,以上就是一种裸机的 cpu loading 监控组件分享了,有问题欢迎大家提 pr !

相关推荐
Einstenn2 小时前
f1c100s tina usb 接口 rtl8723du模组移植调试
驱动开发·wifi·rtl8723
DIY机器人工房12 小时前
NAT 模式、命令行版、桥接模式方式给ubuntu虚拟机配网步骤:
linux·网络协议·ubuntu·嵌入式·桥接模式·diy机器人工房
小柯博客1 天前
从零开始WebRTC(一)
stm32·单片机·嵌入式硬件·青少年编程·嵌入式·webrtc
大聪明-PLUS1 天前
如何从头开始开发 Linux 驱动程序
linux·嵌入式·arm·smarc
一枝小雨2 天前
STM32启动流程全面解析:从上电复位到进入main函数
stm32·单片机·嵌入式·bootloader·启动流程·启动代码·中断向量
Aczone282 天前
驱动(二)Linux 系统移植、驱动开发框架
linux·运维·驱动开发
资源开发与学习2 天前
嵌入式STM32工程师系统养成–实战训练营-9周达成
嵌入式
天山老妖的混世牛魔王2 天前
KMDF驱动编写遇到的第一个编译问题
c++·驱动开发