CPU占用率计算算法

方法1: 使用CPU的处理能力基准计算实时CPU占用率
具体描述:
     (1) 在RTOS系统启动前, 使用Tick中断测试CPU的处理能力基准 CPUPerformanceBase;
     (2) 在系统进入运行后, 使用空闲任务执行与测试CPU处理能力基准完全相同的算法, 得到RTCPUPerformance.
     (3) 周期地计算CPU占用率, 并清除RTCPUPerformance的值, 一般每秒钟计算一次:

RealTime CPU Load = 1 - (RTCPUPerformance/CPUPerformanceBase) * 100%


优点:
     (1) 实现简单
     (2) 所得到的CPU占用率非常准确, 误差只取决于CPUPerformanceBase的测试结果和整除时的余数, 通常误差小于1%
     (3) 不占用硬件资源    
缺点:
     (1) CPU必须一直全速运行, 不能修改CPU主频, 也不能使CPU进入掉电保护模式
     (2) 不能得到系统中每个任务对CPU占用率的贡献
     (3) 必须有一个空闲任务才能计算
评价:
     这个算法只适用于工控, 电信等对不需要使CPU进入掉电保护模式的领域.

方法2: 在Tick中断中对RTOS中的任务进行采样
具体描述:
     (1) 系统进入运行后, 每次Tick中断发生时, 采样一下当前正在执行的任务, 如果CPU处于HALT态, 累加haltTimes
     (2) 周期性地计算CPU占用率, 一般每秒钟计算一次, 并清除haltTimes:(tickIntFrequance表示Tick中断的发生频率)
         RealTime CPU Load = haltTimes / tickIntFrequance
         某个任务对CPU占用率的贡献 = 一个周期内该任务被采样到的次数 / tickIntFrequance * 100%        
优点:
     (1) 实现简单
     (2) 支持CPU掉电模式
     (3) 可以大致得到每个任务对CPU占用率的贡献
缺点:
     (1) 误差取决于Tick的频率和OS中每个任务的运行时长, 因此误差非常大
评价:
     这个算法适用于对CPU占用率精度要求不高的消息电子产品.

方法3: 精确计算每个任务对CPU占用率的贡献
具体描述:
     (1) 除Tick中断外,另开一个比Tick中断频率快若干倍的周期中断(就叫AUXTimer中断吧), 这个中断只对一个计数器执行一次累加.
     (2) 在OS每次执行任务切换时读取该计数器的值(AUXTimer), 并保存到TCB中, 比如, 从任务Task1切换到任务Task2, 算法如下:    
         Task1, 换出动作:
         task1的结束运行时间 = AUXTimer的当前值
         task1的总运行时间 = task1的总运行时间 + task1的结束运行时间 - task1的开始运行时间    
         Task2, 换入动作:
         task2的开始运行时间 = AUXTimer的当前值        
         (以上算法中没有考虑数字回绕, 在工程实现时应当考虑, 发生回绕后任务的结束运行时间小于任务的开始运行时间.    
     (3) 周期性地计算CPU占用率, 一般每秒钟计算一次, 并清除每个任务的总运行时间, 下面的公式中, 一个周期内的总时间等于AUXTimer周期除以Tick周期得到的倍数:        
         某个任务对CPU占用率的贡献 = 一个周期内该任务的总运行时间 / 一个周期内的总时间      
         RealTime CPU Load = 所有任务的CPU占用率之和        
     对这个方法进行简单改进, 就可以实现对CPU占用率进行实时测量, 看官自己动动脑筋吧.    
优点:
     (1) 误差取决于AUXTimer中断的频率, 可以非常精确地得到每个任务对CPU占用率的贡献。

基于方法1的计算过程:
windows2000任务管理器可以看到CPU的占用率。
     CPU是不能间断运行的,只要CPU上电就要运行指令,即使无事可做,也要执行NOP操作。所以windows的CPU占用率低并不是指CPU目前无事可做,而是指CPU可以从当前状态中腾出多少时间来做用户的事情。
     在多任务操作系统中的进程一般都实现了优先权算法,抢占式实时操作系统的高优先权进程能够立即抢占低优先权进程的CPU,而分时操作系统则一般等到当前进程的CPU时间片用完后再调度,但无论实时系统还是分时系统,高优先权进程在分配CPU时间上都比低优先权的进程占优势。
     可以定义PRIORITY_HIGHEST为操作系统定义的最高优先权,PRIORITY_LOWEST为操作系统定义的最低优先权,一般PRIORITY_HIGHEST的值就是0,PRIORITY_LOWEST是大于0的一个数,虽然这样会或多或少混淆HIGHEST和LOWEST的意思。在windows中PRIORITY_HIGHEST为0,是系统进程;PRIORITY_LOWEST不知道是多少,但一定是空闲进程,就是任务管理器中看到System Idle Process进程。在比PRIORITY_LOWEST低一级的(PRIORITY_LOWEST-1)有一个统计进程,姑且叫OSStat,就是用来统计CPU占用率的。
     在OS初始化时一定有一段时间是只有系统进程在运行的,因为初始化还没完成,没开始调度用户进程。这个时候OS可以对CPU进行计时,计算CPU一秒内可以执行多少次加法,比如可以写这样的代码

1 OSIdleCtr = 0L;          

 2 OSTimeDly(OS_TICKS_PER_SEC); 

 3 OSIdleCtrMax = OSIdleCtr;


     其中OSTimeDly是使系统所有进程(除Idle进程外)都挂起OS_TICKS_PER_SEC个时间片,而OS_TICKS_PER_SEC的值可以通过晶振的频率直接计算得到指令周期再由CPU划分,也可以通过CPU提供的指令得到指令周期再由CPU划分。
     在Idle进程中加入这样的代码
OSIdleCtr++;
     到这里OSTimeDly(OS_TICKS_PER_SEC)的意义就很明确了,由于挂起其他进程,这样系统中只有Idle在运行,Idle对OSIdleCtr累加就不会被其他更高优先级的进程中断而造成计算不准确。
     如此就得到了1秒中Idle进程可以执行的最大加法次数,为什么Idle进程对OSIdleCtr的累加就是CPU一秒内最大加法次数呢,因为其他进程都被挂起了1秒,在这1秒内CPU只执行Idle的代码嘛,所以Idle对OSIdleCtr的累加可以代表一个系统的最大执行能力,不可能有其他方式可以在1秒内使OSIdleCtr的值累加到超过这个值。
     另外Idle的其他代码不会影响OSIdleCtr的准确性,因为可以把定义‘系统执行加法的最大次数’改成‘系统执行Idle过程的最大次数’,意义是不变的,仍然代表了CPU的执行能力。
     事实上Idle也是一个进程,所以也参与进程的调度算法,既然已经在Idle中加入了OSIdleCtr累加的算法,那么系统只需要在每秒钟中断的时候将OSIdleCtr初始化为0,则在系统正常运行的时候(用户进程已经开始)1秒钟内Idle对OSIdleCtr的累加就可以看成在普通用户状态下的CPU在1秒内的处理能力,因为这个时候用户进程已经开始参与调度,而Idle进程是系统中最低优先级的进程,其只在无用户任务的情况下调入,只要用户有其他任务,Idle都会挂起来等待,所以这个时候Idle对OSIdleCtr的累加就代表了在当前任务压力下CPU的空闲程度。因为OSIdleCtr每秒钟被复0一次,所以CPU的占用率自然也可以写成
     CPUUsage%=100*(1-OSIdleCtr/OSIdleCtrMax)这条公式的计算写在OSStat里,就形成了windows的CPU占用率形式。
     事实上,上面讨论的算法是ucos2系统的CPU占用率算法,ucos2是一个开源实时操作系统,通常用在嵌入式CPU中。