| 网站首页 | VB.Net实例分析 | .Net技术文摘 | 下载中心 | VB.Net书籍笔记 | .Net源码 | VBS脚本与组件调用 | Windows2008技术文摘 | 给我们留言 | 
最新公告:

  没有公告

您现在的位置: 乐博网 >> VB.Net实例分析 >> WMI编程 >> 实例分析正文
最新推荐 更多内容
WMI获取系统性能信息的编程经验
WMI获取系统性能信息的编程经验
作者:佚名    来源:wangjianying     更新时间:2011-4-6
  摘要:WMI 提供了一整套易于使用的性能计数器类。本文介绍如何利用这些类创建监视脚本来监视处理器使用情况、内存使用情况、网络连接、磁盘使用情况、Web 服务性能、服务器连接、打印机使用情况以及其他各种性能参数。


    好久不见,大家好吗?很抱歉,很久没来这里了。但是,坦率地讲,我们这些 Scripting Guys 还有许多其他重要任务,这些任务比脚本编写工作更重要,因此我们不得不将诸如 Scripting Clinic 专栏之类相对次要的工作先放在一边,而专注于真正有益于 Microsoft 的事情。您不会认为 Bill Gates 的车会自行清洗吧?

    然而,实际上 Bill 的车确实会自行清洗。当然,大多数其他 Microsoft 执行官则没有这么好的车,结果,他们就只能自己一次又一次地洗车。随着天气越来越冷,草长得也不那么快了,因此我就有时间,可以再次坐到计算机前,打造一个全新的 Scripting Clinic 专栏。

    但是,在开始之前,我需要确保阅读此专栏的每个人都熟悉《Frankenstein》(英文)故事。现在,无疑你们都读过了这本书(当然,这也意味着你们都看过了这部电影)。但是为了预防万一,我们还是简单回顾一下:一位名叫 Victor Frankenstein 的雄心勃勃的青年科学家决定收集一些死尸,然后从每个死尸上截取最好的部分并将这些部分缝合在一起,再赋予这个“人”生命,来创造一个超人。(顺便提一句,这个过程与 Peter Costantini 加入最初只有四个人的 Scripting Guys 团队非常相似。)事情最初进展得非常顺利,但是您知道当赋予死尸以生命后会怎样:无论初衷如何,坏事总是坏事。简而言之,情况很快就变得不可收拾,最后甚至每个相关的人都死了。

    当然,《Frankenstein》的寓意是很清楚的:有些事情人们根本就不应该去尝试;特别是像“让死尸复活”这样的想法。(或者至少,如果您要让死尸复活,最好从一些较小且容易控制的对象开始,例如沙鼠或仓鼠。)从《Frankenstein》中我们所有人都可以得到这样一个教训:有些事情最好不要去做。这是我每次接受新任务时都试图遵循的一点。

    那么,为什么我要谈论 Frankenstein 而不去打扫人行道、喝杯咖啡或是做些有用的事呢?其实,很少有人会注意到这一点,Mary Shelley 实际上已经开始了《Frankenstein》续集的创作,但她从未完成。在续集中,Shelley 不是让死尸复活,而是要做一件更加恐怖的事。Shelley 最终放弃了这本书,学者们认为其原因是她感到根本没有人能够承受这种恐惧。这同样说明了相同的问题:有些事情人们没有必要去尝试,这也包括编写用于监视性能的 WMI 脚本。

    文学上的巧合:您知道吗?Mary Shelley 在写《Frankenstein》时只有 19 岁,而 Microsoft Scripting Guys 的每个成员也恰好是这个年龄!
Shelley 没有坚持写完她的书,而许多人甚至可能没有意识到可以使用 WMI 来监视性能。但这却是真的。从 Microsoft® Windows® 2000 开始,Microsoft 向 WMI 中添加了称为 Win32_PerfRawData 的性能监视类。这些 Win32_PerfRawData 类能够让您做什么?其实最好这样问:它们不能让您做什么?使用 Win32_PerfRawData 类,您可以编写能够脚本来完成性能监视器所能完成的所有任务。您要监视什么?处理器使用情况、内存使用情况、网络连接、磁盘使用情况、Web 服务性能、服务器连接或打印机使用情况?Win32_PerfRawData 类可用于监视所有这些内容以及其他更多内容。此外,因为它们是 WMI 类,非常有利于脚本编写者使用。这些类确实很不错。

    注意:这意味着我们要展示给您的脚本可以在 Windows 2000、Windows XP 以及 Microsoft® Windows Server™ 2003 上使用。但是很抱歉,它们不能在 Windows NT 4.0 或 Windows 98 上使用。
那么,如果通过 WMI 进行性能监视有这么好,为什么没有人听说过它?为什么性能监视脚本示例在 Script Center 中并非随处可见?为什么没有人使用这种技术?我们这样来解释一下:让死尸复活,听起来是一个好主意,但是当 Victor Frankenstein 尝试后,他发现有许多意想不到的后果。而这也是一样:尝试编写 WMI 脚本来监视性能的人发现了许多意想不到的后果。

    让我们看一个经典的示例,即,使用一个性能监视类来确定磁盘驱动器上的磁盘可用空间百分比。以下是一个看上去会返回磁盘驱动器上的磁盘可用空间百分比的脚本:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery _
    ("Select * from Win32_PerfRawData_PerfDisk_LogicalDisk " _
        & "Where Name = 'C:'")
For Each objDisk in colDisks
    Wscript.Echo objDisk.PercentFreeSpace & "% 的空间可用。"
Next

    那么,如果在一台总硬盘空间为 11.5 GB 而磁盘可用空间为 4.2 GB 的计算机上运行此脚本,会发生什么情况呢?我们不假思索就可以算出大约有三分之一 (33%) 的磁盘空间未被使用,而脚本报告的结果为:

 


图 1:错误地报告磁盘可用空间百分比

    竟然有 4145% 磁盘可用空间!而我们一直认为 100% 是所能获得的最大百分比。(看上去似乎您在高中所学的东西中有 92% 都没有什么用,对不对?)

    但是不要太激动;您还没有发现某些新的磁盘压缩方法的强大功能呢。其原因是,Win32_PerfRawData 类正如其名称所暗示的:它们是“原始”数据类,这意味着返回的值并不一定是最终的值。您通常需要对其进行“修正”;也就是说,您必须通过某种数学公式对返回的数值进行计算以便获得真正的值。在本例中,我们需要将所获得的值 4145 乘以 100 并除以 11507,才能获得实际的磁盘可用空间百分比,即,更为真实的 36%。

    注意:不必担心,我们会解释所有这些数值都是怎么来的。
现在,如果所有性能计数器都使用相同的数学公式,则情况可能不会那么糟;您可以编写一个函数,通过该函数对返回的数据进行计算,然后报告“修正”后的结果。当然,这有些麻烦,但是没有更好的方法了。

    但实际情况是,并非所有的性能计数器都是相同的:有许多不同的性能计数器类型可供软件开发人员使用,并且这些不同的计数器类型似乎都使用了不同的数学公式。换句话说,您会(至少在理论上)发现自己在每个脚本中编写了 30 个函数,具体数目取决于您试图监视的内容。这让人感到无法承受,以至于大多数人甚至在开始之前便放弃了。

    但是,有些事情是大多数人所不知道的。在那本书中,Frankenstein 创造的怪物并不是一个真正的怪物。实际的情况是,每个人都认为他是一个怪物,并且让这种想法影响了他们的行为。使用 WMI 来监视性能也是如此。它其实不是那么糟;只是每个人都这样认为,并且让这种想法影响了他们的行为。但实际的情况是:使用 WMI 来监视性能并不是那么困难。实际上,当阅读完此本专栏后,您将能够充分利用 Win32_PerfRawData 类来编写脚本 - 我保证。

    注意:保证归保证,修行在个人。
等等,您可能会说:如果有 9 亿种不同的性能计数器类型可供软件开发人员使用呢?当然,这是正确的(可能有几亿的出入)。但是,实际使用的却只是其中的少数类型。事实上,您完全可以忽略绝大部分计数器类型。不相信吗?我们拿两台计算机,一台 Windows XP Professional 计算机和一台 Windows Server 2003 计算机,并使用一个脚本(稍后将展现给您)从各种 Win32_PerfRawData 类来检索各个性能计数器及其类型。以下是我们发现的结果汇总:

计数器类型 XP 上的实例 2003 上的实例
PERF_COUNTER_RAWCOUNT 475 750
PERF_COUNTER_COUNTER 211 320
PERF_COUNTER_LARGE_RAWCOUNT 97 122
PERF_COUNTER_BULK_COUNT 63 78
PERF_RAW_FRACTION 13 30
PERF_100NSEC_TIMER 23 23
PERF_PRECISION_100NS_TIMER 8 8
PERF_AVERAGE_BULK 6 6
PERF_AVERAGE_TIMER 6 6
PERF_COUNTER_100NS_QUELEN_TYPE 6 6
PERF_SAMPLE_FRACTION 0 5
PERF_ELAPSED_TIME 4 4
PERF_COUNTER_TIMER 0 2
PERF_100NSEC_TIMER_INV 1 1
PERF_COUNTER_RAWCOUNT_HEX 1 1
PERF_COUNTER_LARGE_RAWCOUNT_HEX 1 1
PERF_COUNTER_LARGE_QUELEN_TYPE 0 0
PERF_COUNTER_TIMER_INV 0 0
PERF_COUNTER_TEXT 0 0
PERF_COUNTER_MULTI_TIMER_INV 0 0
PERF_COUNTER_DELTA 0 0
PERF_COUNTER_LARGE_DELTA 0 0
PERF_SAMPLE_COUNTER 0 0
PERF_COUNTER_QUELEN_TYPE 0 0
PERF_PRECISION_SYSTEM_TIMER 0 0
PERF_OBJ_TIME_TIMER 0 0
PERF_COUNTER_MULTI_TIMER 0 0
PERF_100NSEC_MULTI_TIMER 0 0
PERF_100NSEC_MULTI_TIMER_INV 0 0
PERF_COUNTER_OBJ_TIME_QUELEN_TYPE 0 0


   如果看一下该表,您会发现一些有趣的事情:六种计数器类型(PERF_COUNTER_RAWCOUNT、PERF_COUNTER_COUNTER、PERF_COUNTER_LARGE_RAWCOUNT、PERF_COUNTER_BULK_COUNT、PERF_RAW_FRACTION 和 PERF_100NSEC_TIMER)几乎涵盖了 Windows XP 和 Windows Server 2003 上使用的所有性能计数器。(您可能还注意到,许多有效的计数器类型甚至没有使用。)在 Windows XP 上,96% 的性能计数器都属于这六种类型之一;在 Windows Server 2003 上,超过 97% 的计数器都属于这六种类型之一。也就是说,您只需了解如何使用这六种类型便可以监视几乎所有内容的性能。它是如此简单,甚至 Scripting Guys 中的一个成员就可以完成它!

    实际上,它甚至比这还要简单。有两种最常用的类型(PERF_COUNTER_RAWCOUNT 和 PERF_COUNTER_LARGE_RAWCOUNT)不需要任何计算;您可以直接使用返回的值。其他两种类型(PERF_COUNTER_COUNTER 和 PERF_COUNTER_BULK_COUNT)使用相同的公式。所以现在,我们只需了解三个公式。这样,当阅读完本文后,您将能够监视性能而无需了解任何内容。

    为使事情更简单,我们将向您展示如何使用其中的各种计数器类型来编写脚本。然后,我们将说明如何确定给定性能计数器的计数器类型。那时,您就可以让“死尸”复活了。或者,编写性能监视脚本。就像他们说的,一切顺利。

    注意一个问题
    在开始之前,我们需要指出一个重要问题,它适用于您使用的任何性能计数器类型。以下是一个看上去非常简单的脚本。它连接到 Win32_PerfRawData_PerfOS_Memory 类,然后回显 AvailableMbytes 计数器的值。(本示例选择 AvailableMbytes 是因为它是一个不需要修正的计数器。)此脚本将暂停 5 秒,然后不断回显 AvailableMbytes 的值,直到完成 20 次这种测量。

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
    ("Select * From Win32_PerfRawData_PerfOS_Memory")
For i = 1 to 20
    For Each objItem in colItems
        intValue = objItem.AvailableMbytes
        Wscript.Echo "可用内存 = " & intValue & " MB"
        Wscript.Sleep(5000)
    Next
Next

    假设您运行此脚本。在第一次循环中,如果您有 53 MB 的可用内存,则脚本将正确报告该情况。在第二次循环中,脚本仍将报告可用内存为 53 MB。实际上,在每次循环中它都将报告可用内存为 53 MB。您可以启动或停止程序;该脚本仍将报告可用内存为 53 MB。您可以启动服务、停止服务、通过网络复制 Windows Media 文件,甚至可以拆下计算机的外壳并取出所有 RAM 芯片,该脚本仍将报告可用内存为 53 MB。无论您做什么,该脚本都将“固执地”认为计算机有 53 MB 的可用内存。

    为什么是这样呢?我们仔细看一下前面脚本中的粗体行。您将注意到,在前面,我们检索了 Win32_PerfRawData_PerfOS_Memory 类的所有实例。结果,这将在我们运行 ExecQuery(您会看出它很重要,因为我们采用了斜体)时获得 Win32_PerfRawData_PerfOS_Memory 属性值的快照。然后,我们设置了一个循环 20 次的 For-Next 循环;在每次循环中,我们都报告 AvailableMbytes 属性的值。

    这看上去似乎没有问题,但实际上我们在这里犯了一个严重错误:我们只运行了一次 ExecQuery,这意味着我们只获得了内存性能计数器的一个快照。此后,我们从未获得过其他快照;从未检索过一组新的值。而是仅具有一个快照,并且只是反复显示该快照。在我们的 For-Next 循环中,回显了 AvailableMbytes 的值,但每次它都是相同的值;我们从未做过任何操作来更新它。如果我们在第一次循环中获得的是 53,则该脚本每次都会报告 53,因为我们没有废除它,也没有检索最新的一组数据。只是反复显示相同的内容。就像网络电视一样。

    如果这有些让人困惑,我们来看一个能够在每次循环中正确报告可用内存的脚本,这可能会有所帮助。请注意 ExecQuery 方法在此脚本中的位置:它位于 For-Next 循环的内部。这意味着我们将运行 20 次 ExecQuery(每次循环运行一次)。这样,每次我们运行循环,都会获得 AvailableMbytes 的当前值(即一个全新的快照)。结果,每次我们都将回显可用内存的真实数量,而不仅仅是第一次启动脚本时的可用内存数量。

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
For i = 1 to 20
    Set colItems = objWMIService.ExecQuery _
        ("Select * From Win32_PerfRawData_PerfOS_Memory")
    For Each objItem in colItems
        intValue = objItem.AvailableMbytes
        Wscript.Echo "可用内存 = " & intValue & " MB"
    Next
    Wscript.Sleep(5000)
Next

    要弄清楚这是如何工作的,最好是您自己尝试一下。在您的计算机上复制并运行这两个脚本。当这两个脚本运行时,打开并关闭一些程序。您会看到,在运行第二个脚本时,可用内存的数量将发生变化,而在运行第一个脚本时不会发生变化。这个示例说明:确保您的性能监视脚本始终检索当前的一组性能值。要实现此目的,请在每次要回显一组新值时都运行 ExecQuery。

    说明:的确,这有一些麻烦,并且如果您从多个类收集性能计数器(我们将在稍后讨论此问题),则会难以跟踪。在 Windows XP 和 Windows 2003 中,有一个新的 WMI 对象(SWbemRefresher 对象)可为您处理好这一切。您只需告诉 SwbemRefresher 要跟踪什么计数器,然后当任何时候希望检索所有性能计数器的一组最新值,只需在脚本中使用一行代码(它将调用该对象的 Refresh 方法)。在以后的专栏中,我们将告诉您如何完成此任务。
现在,我们快速了解一下这六种最常用的计数器类型。

PERF_COUNTER_RAWCOUNT
    可供您使用的性能计数器半数以上都是这种类型,这很好,因为这些计数器不需要进行数学计算,也不需要念什么咒语或任何其他类型的神奇转换。您只需检索值并直接使用它。如果此脚本报告 Microsoft® Word 打开了 916 个句柄,那么打电话给您的赌友,因为您可以打赌 Word 确实打开了 916 个句柄。

    注意:请不要打电话给您的赌友。让您下这种赌注我不能心安,特别是当我押了 5000 美元认为幼兽队能赢得世界职业棒球锦标赛后。
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
For i = 1 to 5
    Wscript.Sleep(1000)
    Set colItems = objWMIService.ExecQuery _
       ("Select * From Win32_PerfRawData_PerfProc_Process Where " & _
            "Name = 'WINWORD'")
    For Each objItem in colItems
        intValue = objItem.HandleCount
        Wscript.Echo "句柄数 = " & intValue
    Next
Next

    如果您认为:“哼,这对我来说类似于原来的常规 WMI 脚本”,则您猜会怎样:您是绝对正确的。我们将连接到 WMI 服务,从 Win32_PerfRawData_PerfProc_Process 类获取数据,然后回显属性值 HandleCount。这与大多数脚本之间的唯一不同在于:我们在循环内执行所有这些工作;这样,我们便可以多次回显句柄计数。(如果句柄计数稳定增长而从不下降,则通常表明存在内存泄漏。)

    注意:在上述脚本中,我们只对 Word 中的情况感兴趣,因此我们包含了 Where 子句 Where Name = 'WINWORD'。这样,该脚本将只报告 Winword.exe 的进程数据。如果我们要获得计算机上当前运行的所有进程的类似性能数据,该如何做?这不是什么问题,只需略去 Where 子句即可:
Set colItems = objWMIService.ExecQuery _
       ("Select * From Win32_PerfRawData_PerfProc_Process")

    当然,如果您这样做了,则可能应当同时回显进程名称和句柄计数。这样,您便可以将句柄计数与进程相匹配。

PERF_COUNTER_LARGE_RAWCOUNT
这是另一个可直接使用的计数器类型;您只需检索并报告值。那还有什么可怕的吗?

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
For i = 1 to 5
    Wscript.Sleep(1000)
    Set colItems = objWMIService.ExecQuery _
        ("Select * From Win32_PerfRawData_PerfProc_Process Where " & _
            "Name = 'WINWORD'")
    For Each objItem in colItems
        intValue = objItem.WorkingSet
        Wscript.Echo "工作集 = " & intValue
    Next
Next

    PERF_RAW_FRACTION
    到目前为止,一切都很好。无需付出太多努力,就可以获得许多有用的性能信息。但是对于 PERF_RAW_FRACTION,就没有这么好了;我们不得不开始第一次计算。但是,老兄,这又有什么难的?

(100 * CounterValue) / BaseValue

    困难之处在于弄清变量代表的内容。因此,我们将告诉您这些。在此公式中,CounterValue 代表性能计数器的值,而 BaseValue 数值用于保证计算的结果是正确的。坦白地讲,我们并不确切知道 BaseValue 是如何计算出来的。幸运的是,我们不需要知道它是如何计算出来的,只要我们能够找出该值是什么就行,我们马上向您展示如何完成此任务。

    以下是该公式在某个实际脚本中的样子:我们连接到 WMI 服务,从 Win32_PerfRawData_PerfDisk_LogicalDisk 类获取数据,获取 PercentFreeSpace 计数器的值,将其乘以 100,然后将所得的结果除以基础值 (11507)。

intBaseValue = 11507
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery _
    ("Select * from Win32_PerfRawData_PerfDisk_LogicalDisk Where Name = 'C:'")
For Each objDisk in colDisks
    dblActualFreeSpace = (100 * objDisk.PercentFreeSpace) / intBaseValue
    Wscript.Echo Int(dblActualFreeSpace)
Next

    听起来好像有许多工作要做,但实际上没有那么复杂。我们假设 PercentFreeSpace 返回的值为 4145。在这种情况下,计算将如下所示:

(100 * 4145) / 11507
或者:
414500 / 11507 = 36
实际上,除以下一点外,这是非常简单的:如何知道 PercentFreeSpace 的基础值为 11507?我查阅了一下。只要您具有一个 PERF_RAW_FRACTION 性能计数器,就会还具有一个伴随的基础值性能计数器。要找到该计数器,只需打开 Wbemtest 进行查看即可:

图 2:在 Wbemtest 中找到基础值性能计数器

    那么,我们接着如何获得 PercentFreeSpace_Base 的值呢?在 Wbemtest 中单击 Instances(实例)按钮,然后双击所得到的任何实例(单击任何一个都可以)。PercentFreeSpace_Base 的值就是您将在脚本中使用的基础值。

 

图 3:确定基础值性能计数器的值

    这里只有一件事情需要注意:PercentFreeSpace_Base 的值会随着您的硬件、操作系统、时间以及其他内容的不同而不同。(实际上,它取决于您的逻辑磁盘的大小。)因此,您不能将其硬编码为某个数值(例如 11507,这只适用于特定大小的磁盘),而应当使用 PercentFreeSpace_Base 的检索值。请注意以下脚本中的粗体行。

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery _
    ("Select * from Win32_PerfRawData_PerfDisk_LogicalDisk Where Name = 'C:'")
For Each objDisk in colDisks
    intBaseValue = objDisk.PercentFreeSpace_Base
    dblActualFreeSpace = (100 * objDisk.PercentFreeSpace) / intBaseValue
    Wscript.Echo Int(dblActualFreeSpace)
Next

    提示:如何知道脚本是否返回了正确的数据?这里有一种快捷方法可以仔细检查您的工作。启动性能监视器并加载您的脚本中正在运行的性能计数器。同时运行该脚本,观察并比较您的值与性能监视器的值。这将有助于您知道返回的数据是否正确。
PERF_COUNTER_COUNTER
仍然在看吗?好的。接下来,这个类型看上去可能有点难对付,但是不必惊慌。做一次深呼吸并保持冷静,您会发现根本不像您开始想象的那样糟。

    PERF_COUNTER_COUNTER(与 Boutros Boutros-Ghali [英文] 没有关系)用于测量每秒发生的事件数(“事件”的定义视不同的计数器而定)。让我们暂时忘记性能监视(你们中的某些人可能在几页之前就已经忘记了),考虑一下将如何测量每秒发生的事情数。这实际非常简单。

[1] [2] [3] [4] 下一页

  • 上一篇:

  • 下一篇: 没有了
  • 【字体: 】【打印此文】【关闭窗口
      相关文章:(只显示最新16条)
    计数器类型使用详解(Win32_PerfRawData)
    采用WMI监视系统性能的实现方法
    性能计数器编程(VB2010实例)

    | 设为首页 | 加入收藏 | 联系站长 | | 友情链接 | 版权申明 |
    乐博网欢迎各种媒体转载我们的原创作品[转载请注明出处];我们鼓励更多VB.Net开发者一起加入研究与探讨;如发现文章访问错误、内容错误或版权疑问、内容有违相关法律(如涉及政治、色情、反动或散布虚假有害信息)等情况,请及时向我们举报,我们将及时纠正!
    联系邮箱:Shiny#vip.qq.com (#替换为@) QQ交流群: 40797788 [闽ICP备05014267号]