var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-333696-1']); _gaq.push(['_trackPageview']); _gaq.push(['_trackPageLoadTime']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
  • 2007年07月21日

    控制CPU使用率

    分类:

    Windows下能用:

    1.   Performance Data Helper系列函数XP SP1后能用的API-GetSystemTimes示例ntdll.dll中未公开的API-NtQuerySystemInformation获得CPU使用率或者WMI(Windows Management and Instrumentation) 获得多核CPU使用率。当超过指定值时就Sleep,通过调整Sleep的时间能够控制CPU不超过阀值,当然CPU使用率肯定是有抖动的。

    2.   作业(Job)-限制进程的CPU使用率,详细请查看Windows核心编程》第五章。由于多任务的实现,Windows在某一时点只运行一个程序,这个时段大约是20毫秒,然后切换到另一个程序,使得"同时"执行多个程序成为可能。所以,所谓CPU消耗就是一个时间函数。具体来看看例子:某个应用程序以10秒为周期使用了5秒钟的CPU时间,那么,平均CPU使用率就是50%。以24小时为周期,就是12小时,如此等等。所以你可以以百分比为度量单位限制CPU使用。技巧就在于选择合适的周期。

    3.   设置SetPriorityClass进程SetThreadPriority线程优先级, 并且使用死循环,让程序始终占用资源,然后中途使用WaitForSingleObject,Sleep等函数来等待或者暂缓线程的执行,用来释放资源。如果希望准确控制某线程在某时刻占用多少资源,应该说是没有这种可能的,WINDOWS是一个分时抢先任务式的操作系统,系统会随时在任意线程之间切换,唯一可以控制的,高优先级的线程优先执行。

     

    执行一个程序,调用CreateProcess激活程序产生一个进程(PDB),系统为此进程建立一个主执行线程(primary thread),执行线程(TDB)才是CPU时间的分配对象。进程拥有优先权等级(priority class),可以在CreateProcess 参数中的dwCreationFlags设定,dwCreationFlags表示创建进程的优先级类别(idle、normal、high、realtime)和进程的类型(控制台进程、调试进程等)。执行线程基本上继承自其父进程的优先权等级,然后再加上CreateThread参数中的dwCreationFlags微调差额(-2~+2)。获得的结果便是执行线程的base priority,范围从0~31数值越高优先权越高。

     

    线程的上下文(Context)结构维护在线程的内核对象中。这个上下文结构反映了线程上次运行时该线程的CPU寄存器的状态。每隔20ms左右,Windows要查看当前存在的所有线程内核对象。在这些对象中,只有某些对象被视为可以调度的对象。Windows选择可调度的线程内核对象中的一个,将它加载到CPU的寄存器中,它的值是上次保存在线程的环境中的值。这项操作称为上下文转换(Context Switches)。
    目前,线程正在执行代码,并对它的进程的地址空间中的数据进行操作。再过20ms左右,Windows就将CPU的寄存器重新保存到线程的上下文中。线程不再运行。系统再次查看其余的可调度线程内核对象,选定另一个线程的内核对象,将该线程的上下文加载到CPU的寄存器中,然后继续运行。当系统引导时,便开始加载线程的上下文,让线程运行,保存上下文和重复这些操作,直到系统关闭。

     

    CPU的运行机制是跟RAM不同的,实现跟RAM那样精确的控制实际上不可行,仍受限于OS的分时性。建议不了解的多看看操作系统原理,然后个人做试验去验证。 

     

    附录:

    1.         CreateProcess这个API 函数有众多参数:

    CreateProcess(

    LPCSTR lpApplicationName,

    LPSTR lpCommandLine,

    LPSECURITY_ATTRIBUTES lpProcessAttributes,

    LPSECURITY_ATTRIBUTES lpThreadAttributes,

    BOOL bInheritHandles,

    DWORD dwCreationFlags,

    LPVOID lpEnvironment,

    LPCSTR lpCurrentDirectory,

    LPSTARTUPINFO lpStartupInfo,

    LPPROCESS_INFORMATION lpProcessInformation

    );

    第一个参数lpApplicationName 指定可执行档档名。

    第二个参数lpCommandLine 指定欲传给新进程的命令列(command line)参数。如果你指定了lpApplicationName,但没有扩展名,系统并不会主动为你加上.EXE 扩展名;如果没有指定完整路径,系统就只在目前工作目录中寻找。但如果你指定lpApplicationName NULL 的话,系统会以lpCommandLine 的第一个「段落」(我的意思其实是术语中所谓的token)做为可执行档档名;如果这个档名没有指定扩展名,就采用预设的".EXE" 扩展名;如果没有指定路径,Windows 就依照五个搜寻路径来寻找可执行文件,分别是:

    1. 调用者的可执行文件所在目录

    2. 调用者的目前工作目录

    3. Windows 目录

    4. Windows System 目录

    5. 环境变量中的path 所设定的各目录

    让我们看看实例:CreateProcess("E:\\CWIN95\\NOTEPAD.EXE", "README.TXT",...);

    系统将执行E:\CWIN95\NOTEPAD.EXE,命令列参数是"README.TXT"。如果我们这样子调用:CreateProcess(NULL, "NOTEPAD README.TXT",...);

    系统将依照搜寻次序,将第一个被找到的NOTEPAD.EXE 执行起来,并转送命令列参数"README.TXT" 给它。

    建立新进程之前,系统必须做出两个核心对象,也就是「进程对象」和「执行线程对象」。

    CreateProcess 的第三个参数和第四个参数分别指定这两个核心对象的安全属性。至于第五个参数(TRUE FALSE)则用来设定这些安全属性是否要被继承。

    第六个参数dwCreationFlags 可以是许多常数的组合,会影响到进程的建立过程。这些常数中比较常用的是CREATE_SUSPENDED,它会使得子进程产生之后,其主执行线程立刻被暂停执行。

    第七个参数lpEnvironment 可以指定进程所使用的环境变量区。通常我们会让子进程继承父进程的环境变量,那么这里要指定NULL

    第八个参数lpCurrentDirectory 用来设定子进程的工作目录与工作磁盘。如果指定NULL,子进程就会使用父进程的工作目录与工作磁盘。

    第九个参数lpStartupInfo 是一个指向STARTUPINFO 结构的指针。这是一个庞大的结构,可以用来设定窗口的标题、位置与大小,详情请看API 使用手册。

    最后一个参数是一个指向PROCESS_INFORMATION 结构的指针:

    typedef struct _PROCESS_INFORMATION {

    HANDLE hProcess;

    HANDLE hThread;

    DWORD dwProcessId;

    DWORD dwThreadId;

    } PROCESS_INFORMATION;

    当系统为我们产生「进程对象」和「执行线程对象」,它会把两个对象的handle 填入此结构的相关字段中,应用程序可以从这里获得这些handles

     

    2.         CreateThread(

    LPSECURITY_ATTRIBUTES lpThreadAttributes,

    DWORD dwStackSize,

    LPTHREAD_START_ROUTINE lpStartAddress,

    LPVOID lpParameter,

    DWORD dwCreationFlags,

    LPDWORD lpThreadId

    );

    第一个参数表示安全属性的设定以及继承,请参考API 手册。Windows 95 忽略此一参数。

    第二个参数设定堆栈的大小。

    第三个参数设定「执行线程函数」名称,而该函数的参数则在这里的第四个参数设定。

    第五个参数如果是0,表示让执行线程立刻开始执行,如果是CREATE_SUSPENDED 则是要求执行线程暂停执行(那么我们必须调用ResumeThread 才能令其重新开始)。最后一个参数是个指向DWORD 的指针,系统会把执行线程的ID 放在这里。

     

    3.         Undocumented Functions for Microsoft Windows NT/2000

    4.          CPU Usage by Processes (qslice.exe)

    5.          PStat

    6.          PsList

    7.          控制CPU占用率曲线改进

    分享到:

    历史上的今天:

    Hierarchy Viewer 揭秘 2012年07月21日
    安装 Android SDK 2011年07月21日
    多音字排序 2010年07月21日
    互联网地狱 2006年07月21日