1.写一个程序,让用户决定windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,机器语言不限。例如:可实现一下三种情况: (1)CPU的占用率固定在50%,为一条直线; (2)cpu的占用率为一条直线,具体占用率由命令行参数决定(参数范围为1-100); (3)CPU的占用率状态是一条正弦曲线。 2分析与解法:让我们仔细思考写程序时曾经遇到的问题,如果不小心写了一个死循环,CPU占用率会跳到最高,并且一直保持在100%,我们也可以打开任务管理器,实际观测一下它是怎么样变动的,凭肉眼观察,它大约是1S重更新一次,一般情况下,CPU使用率会很低,但是,当用户运行一个程序,执行一些复杂的操作的时候,CPU的使用率就会急剧升高,当用户晃动鼠标时,CPU的使用率也有小幅度的变化。那当任务管理器报告CPU的使用率为0的时候,谁使用CPU呢,通过任务管理器的进程(PROCESS)一栏可以看到,system idle process占用CPU空闲的时间,这个学习过操作系统的娃肯定熟悉不过了哈。系统那么多进程,他们什么时候能够闲下来,答案很简单,这些程序或者在等待用户的输入,或者在等待某些事情的发生,或者主动进入休眠状态咯。在任务管理器的一个刷新周期内,CPU忙(执行应用程序)的时间和刷新周期时间的比率就是CPU的占用率,也就是说,任务管理器中显示的是每个刷新周期内的CPU占用率的统计平均值。因此我们可以写一个程序,让它在任务管理器的刷新期间内一会儿忙,一会儿闲。然后调节忙/闲的比例,就可以控制任务管理器中显示CPU占用率。 (1)解法1 要操控CPU的使用率曲线,就需要使CPU在一段时间内(根据task manager的采样率跑BUSY与IDLE两个不同的循环(loop)),从而通过不同的时间比例,来调节CPU使用率。 BUSY LOOP可以通过空循环来实现,idle可以通过sleep()来实现 问题的关键在于如何控制两个loop的时间,我们先测试sleep一段时间,然后循环N次,估算N的值。那么对于一个空循环for(int i=0;i<n;i++);又该如何估算这个最合适的N值呢,我们知道cpu执行的是机器指令,而最接近机器指令的语言是汇编语言,所以我们可以先把这个空循环简答的写成如下汇编代码后再进行分析: loop: mov dx,i inc dx mov i,dx cmp i,n jl loop ;i<n时重复循环 我的CPU是1.28GHZ(1.28*109次个时钟周期每秒)。现在COU每个时钟周期可以执行两条以上的代码,我们取平均值2条,于是有(1280000000*2)/5=512000000(循环每秒),也就是说CPU一秒钟可以运行这个空循环512000000次,但是我们还是不能够将N=512000000,然后sleep(1000)了事,如果我们把CPU工作1s,然后休息1s钟,波形很有可能是锯齿型的,先达到一个峰值,然后跌倒一个很低的占用率。 我们尝试降低两个数量级,令N=51200000,而睡眠时间则相应的改为10ms sleep(10),用10ms的原因是因为比较接近windows的调度时间片,如果选的太小,比如1ms,就会造成线程频繁的被唤醒和挂起,无形中又增加了内核时间的不确定性。本人试过,在N=512000000的时候cpu占用率=100% n=51200000时候cpu占用率≈95%,n=5120000时候,cpu占用率≈50% 代码清单如下: #include "Windows.h" #include "stdlib.h" #include "math.h" int main() { for(; ;) { for(int i=0;i<5120000;i++) ; Sleep(10); } return 0; } 只是约等于哈,因为 我电脑还有点其它东西,做这个程序的时候最好把其它程序乱七八糟的全部关了,才能得到最好的数据,之后就是一直停在50%。 在不断调整5120000的参数后,我们就可以在一台指定的机器上获得一条大致稳定的50%CPU占用率曲线。使用这种方法要注意两点影响: A:尽量减少sleep/awake的频率,以减少操作系统内核调度程序的干扰; B:尽量不要调用system call(比如I/O这些privilege instruction),因为她会导致很多不可控的内核运行时间。 该方法的缺点也很明显:不能适应机器差异性。一旦换了一个CPU我们又得重新估算N值。 解法2:使用GetTickC 我们知道GetTIckCount()可以得到系统启动到现在所经历时间的毫秒值,最多能统计到49.7天。我们可以利用GetTickCount()来判断busy loop 要循环多久 代码如下所示 #include "Windows.h" #include "stdlib.h" #include "math.h" #define ture 1 int main() { int busyTime=10; int idleTime=busyTime; //same ratio will lead to 50%cpu usage _int64 startTime=0; while(ture) { startTime=GetTickCount(); while((GetTickCount() - startTime)<=busyTime) ;//idle loop Sleep(idleTime); } return 0; } 这两种解法都是假设目前系统上只有当前程序在运行,但是实际上操作系统中有很多很多的程序会同时执行各种各样的任务吗,如果此时其它进程使用了 10%,那么这个程序只能使用40的CPU,这样才能达到50%的效果----此时需要一个工具,Perfmon.exeperfmon是从windows NT开始就包含在windows管 理工具组中的专业检测工具之意,perfmon可获取有关操作系统,应用程序以及硬件的各种效能计数器(perf counter)Perfmon的用法相当直接,只要选 择你索要检测的对象,(比如:处理器、ram或者硬盘),或者选择效能计数器(比如监视物理磁盘的平均队列长度)即可。我们可以通过写程序来查询 Perfmon的值,Microsoft .net framework提供了performanceCounter这一对象,可以方便的得到当前各种性能数据,包括CPU的使用率,代码清单如下: // c# code static void MakeUsage(float level) { PerfomanceCountwe p=new PerformanceCounter("Processor","%Processor Time","Total"); if(p==NULL) { return } while(ture) { if (p.NextValue()>level) System.Threading.Thread.Sleep(10); } } 这个似乎是一段c#代码,没学过,也不晓得该带什么头文件,因此没有实验过 好吧,现在可以画一段美好的正弦曲线了: #include "Windows.h" #include "stdlib.h" #include "math.h" #include <stdio.h> #include <tchar.h> const double SPLIT=0.01; const int COUNT =200; const double PI=3.14159265; const int INTERVAL=300; #define ture 1 int _tmain(int argc,TCHAR* argv[]) { DWORD busySpan[COUNT]; 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(ture) { j=j%COUNT; startTime=GetTickCount(); while((GetTickCount()-startTime)<=busySpan[j]) ; Sleep(idleSpan[j]); j++; } return 0; } 用过C的人都知道每一个C的程序都会有一个main(),但有时看别人写的程序发现主函数不是int main(),而是int _tmain(),而且头文件 |
|