在uCOS2操作系统当中可以得到CPU的利用率,计算利用率是通过一个任务来计算的,任务的名字叫“OSTaskStat()”.如果要使用任务需要将OS_CFG.H头文件中的OS_TASK_STAT_EN宏定义为真。这样你就可以在程序中使用任务统计功能了。 如果应用程序打算使用统计任务,那么你必须在主函数当中只建立一开始任务,然后在开始任务中调用OSStatInit(),之后你就可以建立其他任务了。 我们知道如果要使用uCOS2操作系统需要在main()函数中调用OSInit()函数,先把此函数的代码复制如下: void OSInit (void) { OSInitHookBegin(); /* Call port specific initialization code */
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */
#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) OS_FlagInit(); /* Initialize the event flag structures */ #endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0) OS_MemInit(); /* Initialize the memory manager */ #endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0) OS_QInit(); /* Initialize the message queue structures */ #endif
OS_InitTaskIdle(); /* Create the Idle Task */ #if OS_TASK_STAT_EN > 0 OS_InitTaskStat(); /* Create the Statistic Task */ #endif
#if OS_TMR_EN > 0 OSTmr_Init(); /* Initialize the Timer Manager */ #endif
OSInitHookEnd(); /* Call port specific init. code */
#if OS_DEBUG_EN > 0 OSDebugInit(); #endif } 在函数中会看到如下代码: OS_InitTaskIdle(); /* Create the Idle Task */ #if OS_TASK_STAT_EN > 0 OS_InitTaskStat(); /* Create the Statistic Task */ 这两个函数其实就是建立两个任务,一个是空闲任务,一个是任务统计任务。空闲任务是每一个uCOS2应用程序中必须要使用的,但是统计任务可以不用。 在InitTaskIdle()函数中的关键代码如下: (void)OSTaskCreateExt(OS_TaskIdle, (void *)0, /* No arguments passed to OS_TaskIdle() */ &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Top-Of-Stack */ OS_TASK_IDLE_PRIO, /* Lowest priority level */ OS_TASK_IDLE_ID, &OSTaskIdleStk[0], /* Set Bottom-Of-Stack */ OS_TASK_IDLE_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */ 在InitTaskStat()函数中的关键代码如下: (void)OSTaskCreateExt(OS_TaskStat, (void *)0, /* No args passed to OS_TaskStat()*/ &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1], /* Set Top-Of-Stack */ OS_TASK_STAT_PRIO, /* One higher than the idle task */ OS_TASK_STAT_ID, &OSTaskStatStk[0], /* Set Bottom-Of-Stack */ OS_TASK_STAT_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* Enable stack checking + clear */ 从函数的调用参数可以知道统计任务的优先级为OS_TASK_STAT_PRIO,而空闲任务的优先级为OS_TASK_IDLE_PRIO。在Ucos_ii.h中有以下宏定义: #define OS_TASK_STAT_PRIO (OS_LOWEST_PRIO - 1) /* Statistic task priority */ #define OS_TASK_IDLE_PRIO (OS_LOWEST_PRIO) /* IDLE task priority */ 可以看到空闲任务的优先级最低,任务统计的优先级次低。在应用程序中建立的任何其他任务都会比这两个任务的优先级高。
在main函数中只要调用OSStart()函数后,程序就会执行最高优先级的任务,就是在main()函数中建立的开始任务。在此任务中回调用统计任务初始化函数OSStatInit()。这个函数的源码如下: #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0; #endif
OSTimeDly(2); /* Synchronize with clock tick */ OS_ENTER_CRITICAL(); OSIdleCtr = 0L; /* Clear idle counter */ OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC / 10); /* Determine MAX. idle counter value for 1/10 second */ OS_ENTER_CRITICAL(); OSIdleCtrMax = OSIdleCtr; /* Store maximum idle counter count in 1/10 second */ OSStatRdy = OS_TRUE; OS_EXIT_CRITICAL(); 在函数中的OSTimeDly(2);系统调用是为了,同步时钟滴答。因为在开始计算空闲计数器时,产生系统滴答的定时器计数器可能会偏离计数器设定的最大值很多。这样会造成计算出来的空闲计数器最大值误差较大。加上了此函数后,就会减小误差,因为一次滴答结束之后,只需要执行一小段的程序就要执行 OSTimeDly(OS_TICKS_PER_SEC / 10)函数了。这一段小段程序只花费了定时器计数器很少的时间,这要计算出来的空闲计数器的最大值会有很少的偏差,可以忽略不计。 OSTimeDly(OS_TICKS_PER_SEC / 10); 此函数被调用之后开始任务就会处于挂起状态。这样就会执行任务统计这个任务了,在看统计任务之前先看一下空闲任务,空闲任务的源码如下: #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0; #endif
(void)p_arg; /* Prevent compiler warning for not using 'p_arg' */ for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); OSTaskIdleHook(); /* Call user definable HOOK */ } 空闲任务其实执行的就是红色部分的区域,就是对全局变量OSIdleCtr不停的递增。而OSTaskIdleHook函数中是空函数。是为了防止OSIdleCtr可能会溢出,由用户程序在此函数中添加必要的延时。OSIdleCtr在此任务中更新,会在任务统计这任务使用。 任务统计这任务源代如下: void OS_TaskStat (void *p_arg) { #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0; #endif
(void)p_arg; /* Prevent compiler warning for not using 'p_arg' */ while (OSStatRdy == OS_FALSE) { OSTimeDly(2 * OS_TICKS_PER_SEC / 10); /* Wait until statistic task is ready */ } OSIdleCtrMax /= 100L; if (OSIdleCtrMax == 0L) { OSCPUUsage = 0; (void)OSTaskSuspend(OS_PRIO_SELF); } for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; /* Obtain the of the idle counter for the past second */ OSIdleCtr = 0L; /* Reset the idle counter for the next second */ OS_EXIT_CRITICAL(); OSCPUUsage = (INT8U)(100L - OSIdleCtrRun / OSIdleCtrMax); OSTaskStatHook(); /* Invoke user definable hook */ #if (OS_TASK_STAT_STK_CHK_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0) OS_TaskStatStkChk(); /* Check the stacks for each task */ #endif OSTimeDly(OS_TICKS_PER_SEC / 10); /* Accumulate OSIdleCtr for the next 1/10 second */ } }
该任务基本上一直会处在挂起状态,因为它一直在对统计就绪这个全局变量OSStatRdy进行遍历,等待其为真才回执行下面的语句。它什么时候为真呐?在OSStatInit函数中有一个语句OSStatRdy = OS_TRUE;。可以看出当计算出100个滴答空闲计数器的值后并赋值给OSIdleCtrMax 变量。之后就可以执行OSIdleCtrMax /= 100L;了。这个语句是为了得到1个时钟滴答的空闲计数器最大值。然后进入任务死循环for( ; ; ),在此循环中就是一直在延时100个是时钟滴答,然后取出OSIdleCtr值,放在OSIdleCtrRun 变量中,通过OSCPUUsage = (INT8U)(100L - OSIdleCtrRun / OSIdleCtrMax);得出CPU的利用率。
在此要说明的是 OSIdleCtrMax为一个时钟滴答的空闲计数器最大值,而OSIdleCtrRun 为100个时钟滴答的空闲计数器值。 OSIdleCtrRun / OSIdleCtrMax等价于100*(一个时钟滴答的空闲计数器值/(一个时钟滴答空闲计数器的最大值)。(INT8U)(100L - OSIdleCtrRun / OSIdleCtrMax)就是CPU的利用率了。 |