让CPU占用率曲线听你指挥—微软技术面

来源:互联网 发布:淘宝如何做广告 编辑:程序博客网 时间:2024/05/25 08:13

学习自《程序员》2008年03月刊

问题:

写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:

1. CPU的占用率固定在50%,为一条直线;

2. CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1-100);

3. CPU的占用率状态是一个正弦曲线。

分析与解法:

    凭肉眼观察,任务管理器大约1S钟更新一次。当CPU使用率为0的时候,通过“进程”选项卡可以看到,System Idle Process占用了CPU空闲的时间。回忆一下,系统中有那么多进程,它们什么时候能闲下来?要么等待用户输入,要么等待某事件发生(WaitForSingleObject()),或进入休眠状态(通过sleep()实现)。

    在任务管理器的一个刷新周期内,CPU忙的时间/刷新周期总时间=CPU占用率。也就是说,任务管理器中显示的是每个刷新周期内CPU占用率的统计平均值。因此,我们写程序让它在任务管理器的刷新周期内一会儿忙,一会儿闲,通过调节忙/闲比例,控制CPU占用率。

解法一:简单的解法

代码清单1-1 

int main()

{

     for( ; ;)

     {

          for(int i=0; i<9600000; i++)

          sleep(10);

     }

     return 0;

}

 

 

 

 

 

 

 

 

 

 

为什么会选取9600000和10两个参数呢?请听细解:

CPU执行的都是机器指令,而最接近于机器指令的是汇编语言,所以我们可以先把for(int i=0;i<n;i++)写成如下汇编代码再进行分析

loop:

mov dx i  ;将i置入dx寄存器

inc dx      ;将dx寄存器加1

mov i dx  ;将dx中的值赋回i

cmp i n    ;比较i和n

jl loop     ;i小于n时则重复循环

 

 

 

 

 

 

 

 

假设这段代码要执行的CPU是P4 2.4Ghz(即2.4*10^9个时钟周期每秒)。现代CPU每个时钟周期可以执行两条以上的代码(这里按两条算),那么(2.4*10^9)*2/5=960 000 000(循环/秒)。而如果我们直接令n=960 000 000,然后再sleep(1000),即忙1S闲1S,那么波形很可能就是锯齿状的—先达到一个峰值,然后跌到一个很低。于是我们尝试降低两个数量级,即n=n/100;sleep(10).用10ms是因为它比较接近Windows的调度时间片,如果选得太小,则会造成线程频繁地被唤醒和挂起,无形中增加了内核时间的不确定性影响。

     使用这种方法要注意两点:1. 尽量减少sleep/awake的频率,如果频繁发生,影响则会很大,因为此时更高的操作系统内核调度程序会占用很多CPU运算时间;2. 尽量不要调用system call(比如IO这些privilege instruction),因为它也会导致很多不可控的内核运行时间。

     这种方法缺点也很明显:1. 不能适应机器差异性,换了CPU就要重新计算N值;2. 没办法动态了解CPU运算能力,然后自动调节忙/闲比。

解法二:使用GetTickCount()和Sleep()

代码清单1-2

int busyTime=10;         //10ms

int idleTime=busyTime;

 

int64 startTime=0;

while(true)

{

      startTime=GetTickCount();

 

      while( GetTickCount()-starTime <=busyTime ) ;

 

      sleep(idleTime);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

我们知道,GetTickCount()可以得到“系统启动到现在”的毫秒值,最多能够统计到49.7天。另外,利用sleep()最多也只能精确到1ms.因此,可以在“ms”这个数量级上操作和比较。

    以上两种解法都是假设目前系统 上只有当前程序在运行,但实际上,操作系统中有很多程序都会在不同时间执行各种任务。那怎么做呢?我们得知道“当前CPU占用率是多少”,这就要用到另一个工具来帮忙—Perfmon.exe。

    我们可以写程序来查询Perfmon的值,Microsoft .NET Framework提供了PerformanceCounter()这一类型,从而可方便地拿到当前各种计算机性能数据,包括CPU使用率。

解法三:正弦曲线

//C++ code to make task manager

//generate sine graph

#include "Windows.h"

#include "stdlib.h"

#include "math.h"

 

const double SPLIT=0.01;

const int COUNT=200;

const double PI=3.14159265;

coust int INTERVAL=300;

 

int _tmain(int argc, _TCHAR* argv[])

{

     //array of busy times

     DWORD busySpan[COUNT];

     //array of idle times

     DWORD idleSpan[COUNT];

 

     int half=INTERVAL/2;

     double radian=0.0;

     for(int i=0;i<COUNT;i++)

     {

          busySpan[i]=(DWORD)(half+sin(PI*radian)*half);

          idleSpan[i]=INTERVAL-busySpan[i];

          radian+=SPLIT:

      }

 

      DWORD startTime=0;

      int j=0;

      while(true)

      {

            j=j%COUNT;

            startTime=GetTickCount();

            while(GetTickCount()-startTime<=busySpan[j]);

            Sleep(idleSpan[j]);

            j++;

      }

      return 0;

}

原创粉丝点击