写一个程序,让用户来决定 Windows 任务管理器(Task Manager)的 CPU 占用率。程 |
序越精简越好,计算机语言不限。例如,可以实现下面三种情况: |
2. CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1~ 100); |
MSRA TTG(Microsoft Research Asia, Technology Transfer Group)的一些实习生写了各 |
![](https://s1.51cto.com/attachment/201304/230717930.png)
看来这并不是不可能完成的任务。让我们仔细地回想一下写程序时曾经碰到的问题,如 |
果我们不小心写了一个死循环,CPU 占用率就会跳到最高,并且一直保持 100%。我们也可 |
以打开任务管理器 ,实际观测一下它是怎样变动的。凭肉眼观察,它大约是 1 秒钟更新一 |
次。一般情况下,CPU 使用率会很低。但是,当用户运行一个程序,执行一些复杂操作的 |
时候,CPU 的使用率会急剧升高。当用户晃动鼠标时,CPU 的使用率也有小幅度的变化。 |
那当任务管理器报告 CPU 使用率为 0 的时候,谁在使用 CPU 呢?通过任务管理器的“进 |
程(Process)”一栏可以看到,System Idle Process 占用了 CPU 空闲的时间——这时候大家该 |
回忆起在“操作系统原理”这门课上学到的一些知识了吧。系统中有那么多进程,它们什么时 |
候能“闲下来”呢?答案很简单,这些程序或者在等待用户的输入,或者在等待某些事件的发生, |
或者进入休眠状态(通过 Sleep()来实现)。生(WaitForSingleObject()) |
在任务管理器的一个刷新周期内,CPU 忙(执行应用程序)的时间和刷新周期总时间的 |
比率,就是 CPU 的占用率,也就是说,任务管理器中显示的是每个刷新周期内 CPU 占用率 |
的统计平均值。因此,我们写一个程序,让它在任务管理器的刷新期间内一会儿忙,一会儿 |
闲,然后通过调节忙/闲的比例,就可以控制任务管理器中显示的 CPU 占用率。 |
要操纵 CPU 的 usage 曲线,就需要使 CPU 在一段时间内(根据 Task |
Manager 的采样率)跑 busy 和 idle 两个不同的 loop,从而通过不同的时间 |
Busy loop 可以通过执行空循环来实现,idle 可以通过 Sleep()来实现。 |
问题的关键在于如何控制两个 loop 的时间,方法有二: |
Sleep一段时间,然后以for循环n次,估算n的值。 |
那么对于一个空循环 for(i = 0; i < n; i++);又该如何来估算这个最合适的 n 值呢? |
我们都知道 CPU 执行的是机器指令,而最接近于机器指令的语言是汇编语言,所以我们可 |
以先把这个空循环简单地写成如下汇编代码后再进行分析: |
![](https://s1.51cto.com/attachment/201304/231108935.png)
假设这段代码要运行的 CPU 是 P4 2.4Ghz(2.4 * 10 的 9 次方个时钟周期每秒)。现代 |
CPU 每个时钟周期可以执行两条以上的代码,那么我们就取平均值两条,于是让(2 400 000 |
000 * 2)/5=960 000 000(循环/秒),也就是说 CPU 1 秒钟可以运行这个空循环 960 000 000 |
次。不过我们还是不能简单地将 n = 60 000 000,然后 Sleep(1000)了事。如果我们让 CPU 工 |
作 1 秒钟,然后休息 1 秒钟,波形很有可能就是锯齿状的——先达到一个峰值(大于>50%), |
我们尝试着降低两个数量级,令 n = 9 600 000,而睡眠时间相应改为 10 毫秒 |
(Sleep(10))。用 10 毫秒是因为它不大也不小,比较接近 Windows 的调度时间片。如果选 |
得太小(比如 1 毫秒),则会造成线程频繁地被唤醒和挂起,无形中又增加了内核时间的不确定性影响。最后我们可以得到如下代码:
int main() { for(;;) { for(int i = 0; i < 9600000; i++); Sleep(10); } return 0; } |
在不断调整 9 600 000 的参数后,我们就可以在一台指定的机器上获得一条大致稳定的 |
1. 尽量减少sleep/awake的频率,如果频繁发生,影响则会很大,因为此时优先级更高 |
2. 尽量不要调用system call(比如I/O这些privilege instruction),因为它也会导致很多不 |
该方法的缺点也很明显:不能适应机器差异性。一旦换了一个 CPU,我们又得重新估 |
算 n 值。有没有办法动态地了解 CPU 的运算能力,然后自动调节忙/闲的时间比呢?请看下 |
【解法二】使用 GetTickCount()和 Sleep() |
我们知道 GetTickCount()可以得到“系统启动到现在”的毫秒值,最多能够统计到 49.7 |
天。另外,利用 Sleep()函数,最多也只能精确到 1 毫秒。因此,可以在“毫秒”这个量级做 |
利用 GetTickCount()来实现 busy loop 的循环,用 Sleep()实现 idle loop。伪代码如下: |
int busyTime = 10; //10 ms |
int idleTime = busyTime; //same ratio will lead to 50% cpu usage
Int64 startTime = 0; while (true) { startTime = GetTickCount(); // busy loop的循环 while ((GetTickCount() - startTime) <= busyTime) ; |
//idle loop Sleep(idleTime); |
这两种解法都是假设目前系统上只有当前程序在运行,但实际上,操作系统中有很多程 |
序都会在不同时间执行各种各样的任务,如果此刻其他进程使用了 10% 的 CPU,那我们的 |
程序应该只能使用 40%的 CPU(而不是机械地占用 50%),这样可达到 50%的效果。 |
我们得知道“当前CPU 占用率是多少” 这就要用到另一个工具来帮忙——Perfmon.exe。, |
Perfmon 是从 Windows NT 开始就包含在 Windows 服务器和台式机操作系统的管理工具 |
组中的专业监视工具之一(如图 1-2 所示)。Perfmon 可监视各类系统计数器,获取有关操 |
作系统、应用程序和硬件的统计数字。Perfmon 的用法相当直接,只要选择您所要监视的对 |
象(比如:处理器、RAM 或硬盘),然后选择所要监视的计数器(比如监视物理磁盘对象 |
时的平均队列长度)即可。还可以选择所要监视的实例,比如面对一台多 CPU 服务器时, |
![](https://s1.51cto.com/attachment/201304/231539147.png)
我 们 可 以 写 程 序 来 查 询 Perfmon 的 值 , Microsoft .Net Framework 提 供 了 |
PerformanceCounter()这一类型,从而可以方便地拿到当前各种计算机性能数据,包括 CPU |
|
转载于:https://blog.51cto.com/icbcchina/1180282