写一个程序,让用户决定Windows任务管理器Task Manager的CPU占有率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:
1 CPU的占用率固定在50%,为一条直线
2 CPU的占用率为一条直线,具体占用率由命令行参数决定
3 CPU的占用率是一条正弦曲线
分析:
有一名同学写了如下的代码
while(true)
{
if(busy)
i++;
else
}
然后他就陷入了苦苦思索:else干什么呢?怎样才能让电脑不做事情呢?CPU的占用率为0的时候,到底是什么东西在占用CPU?另一名同学花很多时间构想如何“深入内核,以控制CPU占用率”,可是事情真的有这么复杂么?
如果不小心写了一个死程序,CPU占用率就会跳到最高,并且一直保持在100%。一般情况下,CPU占用率会很低,但是,当用户运行一个程序时,执行一些复杂的操作时,CPU的占用率会急剧升高。当用户晃动鼠标的时候,CPU的使用率也会有小幅度的变化。
那当任务管理器报告CPU为0时,到底是谁在使用CPU呢?通过任务管理器的进程process一栏可以看到,System Idle Process占用了CPU的空闲的时间,系统中有那么多的进程,他们什么时候能闲下来呢,答案很简单,这些程序或者在等待用户的输入,或者在等待某些事情发生,或者主动进入休眠状态。
在任务管理器的一个刷新周期内,CPU忙的时间和刷新周期总时间的比率就是CPU的占用率,也就是说,任务管理器中显示的是每个刷新周期内CPU占用率统计平均值。
解法一:
要操纵CPU的占用率曲线,就需要使CPU在一段时间内跑busy和idle两个不同的循环loop,从而实现不同的时间比例。
busy loop可以通过执行空循环来实现,idle可以通过sleep()来实现。
问题的关键在于如何控制两个loop的时间,我们先实验一下sleep一段时间,然后循环n次,估计n 的值。
那么对一个空循环for(i=0;i<n;i++);又该如何来估算这个最合适的n值,我们都知道CPU执行的是机器指令,而最接近机器指令的语言就是汇编语言,所以我们可以先把这个空循环写成汇编代码在进行分析:
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。现代CPU每个时钟周期可以执行两条以上的代码,我们取平均值两条,于是有,(2400000000*2)/5=960000000(循环/秒),也就是说CPU1秒钟可以运行这个空循环960000000次。不过我们还是不能简单地将n=960000000,然后sleep(1000)了事。如果我们让CPU工作一秒钟,然后休息一秒钟,波形很有可能就是锯齿形状的 先达到一个峰值,然后跌倒一个很低的占用率。
我们尝试着降低两个数量级,令n=960000000,而睡眠时间则相应的改为10毫秒,用10毫秒是因为比较接近Windows的调度时间片。如果选的太小,会造成线程频繁的被唤醒和挂起,无形中又增加了内核时间的不确定性。
int main()
{
for(;;)
{
for(int i =0;i<9600000;i++)
;
Sleep(10);
}
return 0;
}
在不断地调整9600000的参数后,我们就可以在一台指定的机器上获得一条大致稳定的50%CPU占用率直线。
使用这种方法要注意两点:
1 尽量减少sleep/awake的频率,以减少操作系统内核调度程序的干扰;
2 尽量不要调用system call,因为他也会导致很多不可控的内核运行时间。
该方法有明显的缺点,不能适应机器差异性。一旦换了一个CPU,我们又得重新估算n值。