计算k210的cpu使用率

rt-thread在k210上运行,并计算cpu使用率

Posted by Hehesheng on February 3, 2020

k210计算cpu使用率

前言

博客停更一年多,快两年,少见的更新一下,表示一下这个博客我还在用,hahahaha

运行环境

找轮子

本着不重复造轮子的准则,我先找找有没有可以重复使用的轮子,果不其然我在rt-thread的官方例程里找到了计算cpu使用率的例子

好,本篇完!

正片

好了,不开玩笑,如果真是这样我还写什么记录。

要知道在这里我们的k210是一片双核400M的芯片,而例程提供的是单核的。

rt-thread提供的例子原理就是先关闭所有中断,然后记录100ms里,cpu对一变量的累加次数得total count,再以此为参考,在之后的每100ms进行累加得count。count值比total count值即为cpu使用率。

原理很简单,理解之后我们进行一些简单的改造,这里,我这里定义了一个结构体

typedef struct cpu_usage
{
    rt_uint64_t total_count;
    rt_uint64_t count;
    rt_uint8_t cpu_usage_major;
    rt_uint8_t cpu_usage_minor;
} cpu_usage;
static cpu_usage cpus[2] = {0};

很好理解,这个结构体记录了各个核心各自的数据,计算的钩子函数也顺理成章的写出来

static void cpu_usage_idle_hook()
{
    rt_tick_t tick;
    rt_uint8_t core = current_coreid();

    if (cpus[core].total_count == 0)
    {
        /* get total count */
        rt_enter_critical();
        tick = rt_tick_get();
        while (rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
        {
            cpus[core].total_count++;
        }
        rt_exit_critical();
    }

    cpus[core].count = 0;
    /* get CPU usage */
    tick = rt_tick_get();
    while (rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
    {
        cpus[core].count++;
    }

    /* calculate major and minor */
    if (cpus[core].count < cpus[core].total_count)
    {
        cpus[core].count           = cpus[core].total_count - cpus[core].count;
        cpus[core].cpu_usage_major = (cpus[core].count * 100) / cpus[core].total_count;
        cpus[core].cpu_usage_minor = ((cpus[core].count * 100) % cpus[core].total_count) * 100 / cpus[core].total_count;
    }
    else
    {
        cpus[core].total_count = cpus[core].count;

        /* no CPU usage */
        cpus[core].cpu_usage_major = 0;
        cpus[core].cpu_usage_minor = 0;
    }
}

我们再写一个查看cpu使用率的函数

static void get_cpu_usage(void)
{
    rt_kprintf("cpu  usage     total\n");
    rt_kprintf("---  ------  ---------\n");
    for (int core = 0; core < 2; core++)
    {
        rt_kprintf(" %d   %02d.%02d%%  %8d\n", core, cpus[core].cpu_usage_major, cpus[core].cpu_usage_minor,
                   cpus[core].total_count);
    }
}
MSH_CMD_EXPORT(get_cpu_usage, get cpu usage);

ok,编写完成,编译,下载!

核心2获取失败

事情并没有那么一番风顺,下载到板子上后具体的效果

msh >get_cpu_usage
cpu  usage     total
---  ------  ---------
 0   04.56%   1115625
 1   00.00%         0

???可以看到核心2的total是0,这意味着钩子函数在核心2上一次都没有运行过。

简单判断一下,应该是钩子函数没有挂上核心2或者rt-thread对核心2根本不支持钩子函数。有了思路就很容易定位到应该是idle线程的处理方式上有一些特别之处,打开源码

static void rt_thread_idle_entry(void *parameter)
{
#ifdef RT_USING_SMP
    if (rt_hw_cpu_id() != 0)
    {
        while (1)
        {
            rt_hw_secondary_cpu_idle_exec();
        }
    }
#endif

    while (1)
    {
#ifdef RT_USING_IDLE_HOOK
        rt_size_t i;

        for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
        {
            if (idle_hook_list[i] != RT_NULL)
            {
                idle_hook_list[i]();
            }
        }
#endif

        rt_thread_idle_excute();
#ifdef RT_USING_PM        
        rt_system_power_manager();
#endif
    }
}

可以看到,对于不是核心1,idle线程都会进入到函数rt_hw_secondary_cpu_idle_exec()中,打开这个函数发现他是一个内联汇编函数,运行指令wfi

void rt_hw_secondary_cpu_idle_exec(void)
{
    asm volatile ("wfi");
}

wfi指令就是wait for interrupt,意思就是等待中断到来,他会停止当前cpu的指令流,直到发生中断,这个指令的目的就是为了降低功耗。wfi有点类似于NOP,不同的是,nop还是在运行指令,而wfi是停止运行了。

找到问题,那我们简单修改一下空闲线程的处理方式就好了。让他非核心1的时候也运行钩子函数。

if (rt_hw_cpu_id() != 0)
{
    while (1)
    {
#ifdef RT_USING_IDLE_HOOK
        rt_size_t i;

        for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
        {
            if (idle_hook_list[i] != RT_NULL)
            {
                idle_hook_list[i]();
            }
        }
#endif
        rt_hw_secondary_cpu_idle_exec();
    }
}

再运行一次

msh >get_cpu_usage
cpu  usage     total
---  ------  ---------
 0   04.73%   1115760
 1   01.76%   1115681
msh >

Great! 这次两块核心的cpu使用率都显示出来了,到这对于k210的cpu使用率的计算就到这里,准备再出一期计算各个线程的cpu使用率的记录,应该最近就出,不过也有可能是有生之年系列,hahahahaha

对于代码我已经上传到我的仓库,同时我对k210的几乎所有魔改都会上传到这个仓库,有兴趣的可以自己去看看。

觉得有意思就点个star吧,秋梨膏