即使进程没有退出,进程的性能计数器实例名称是不是可以更改

Posted

技术标签:

【中文标题】即使进程没有退出,进程的性能计数器实例名称是不是可以更改【英文标题】:Can the performance counter instance name of a process change even if the process has not exited即使进程没有退出,进程的性能计数器实例名称是否可以更改 【发布时间】:2012-08-09 14:36:58 【问题描述】:

我使用这个类作为一类测试的基类,这些测试启动一个进程并给它一些输入,然后等待它变得空闲,然后再给它更多输入。

public abstract class TestProcessLaunchingBase

    protected PerformanceCounter PerfCounter  get; set; 

    protected void WaitForProcessIdle()
    
        while (true)
        
            float oldValue = PerfCounter.NextValue();

            Thread.Sleep(1000);

            float nextValue = PerfCounter.NextValue();

            if (nextValue == 0)
                break;
        
    

    protected void FindSpawnedProcessPerfCounter(int processId)
    
        PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");
        string[] instances = cat.GetInstanceNames();
        foreach (string instance in instances)
        
            using (PerformanceCounter cnt = new PerformanceCounter("Process", "ID Process", instance, true))
            
                int val = (int)cnt.RawValue;
                if (val == processId)
                
                    PerfCounter = new PerformanceCounter("Process", "% Processor Time", instance);
                    break;
                
            

        

        Assert.IsNotNull(PerfCounter, "Failed to perf counter");
    

这些测试偶尔会失败,因为PerfCounter.NextValue() 会抛出一个

System.InvalidOperationException 指定类别中不存在实例“foobar#2”

性能计数器的实例名称似乎不是持久的。

如果存在三个 foobar 进程,它们可能具有实例名称

foobar pid 5331 foobar #1 pid 5332 foobar #2 pid 5333

如果 pid 5332 退出 foobar #2 似乎变成 foobar #1

问题:

    这是记录在案的行为吗?你不能坚持一个性能计数器吗?每次都要查吗?

    或者,是否有一个性能计数器可以为所有名为 foobar

    的进程提供 Processor Time

【问题讨论】:

【参考方案1】:

我过去已经遇到过这个问题。实例名称的ProcessName#InstanceNumber 模式显然是微软的一个糟糕选择,你知道为什么:)

所以基本上你有两个选择:

1) 每次使用您的 FindSpawnedProcessPerfCounter 方法创建一个新的 PerformanceCounter 实例。

2) 按照KB281884 中描述的步骤将模式从ProcessName#InstanceNumber 更改为ProcessName_ProcessID

第一种解决方案的问题是每次构建一个新实例都需要一些CPU时间。

第二种解决方案的问题是注册表修改也会影响所有也在使用此性能计数器的程序。并且它需要在启动您的应用程序之前修改注册表。

您拥有的最后一个选项是根本不使用性能计数器。如果您只对ProcessorTime 信息感兴趣,您可以使用 P/Invoke 调用一些 Kernel32 函数来检索它。

编辑:

Process 类还提供了UserProcessorTimePrivilegedProcessorTime (kernel processor time) 属性。两者都返回 TimeSpan 实例(= 时间量),因此要检索处理器时间的百分比,您必须自己进行一些计算(涉及刷新周期和处理器时间)。

【讨论】:

P/Invoke: GetProcessTimes 为给定进程提供用户和内核模式时间。需要先获取OpenProcess的进程句柄,如果该应用程序的另一个实例关闭,它肯定不会改变。【参考方案2】:

如果您必须使用性能计数器,我的方法是在遇到异常时重新创建我的性能计数器。

当抛出异常时,处理旧的性能计数器,然后使用进程 ID 为 foobar 测试的所有实例创建新的性能计数器实例。这里有一篇不错的 *** 帖子:Performance Counter by Process ID instead of name?

我假设您的声明“启动一个进程并给它一些输入”,即您在启动它后保留了进程 ID。因此,您始终拥有一组要监控的正在运行的测试进程。

使用这种技术,您只会在遇到异常时招致性能损失。

正如@ken2k 已经指出的那样,您可以更改性能计数器的命名约定以保持一致性。以我的经验,性能计数器有时会因为看似意想不到的原因而遇到任何数量的异常。因此,即使您确实更改了性能计数器名称,如果必要的话,能够重新创建您的性能计数器可能会很好。

【讨论】:

我认为通过在异常时重新创建实例来解决问题不是一个好主意。以 OP 为例:如果 ID 为 5333 的进程退出,实例“foobar #2”会抛出异常,很好。但是,如果 ID 5332 的进程退出,使用“foobar #1”不会引发任何异常:它只是“指向”ID 5333 的进程。没有例外,错误的值。 @ken2k,我在我的帖子中暗示了复数。即:重新创建性能计数器的集合,而不仅仅是引发异常的那个。也许我不够清楚。最终,他将希望将结果与进程 ID 联系起来或聚合这些值。如果聚合,他不需要关心 foobar#1 的变化,只要它仍然是一个测试过程。如果他想将数据绑定到进程 ID,他需要执行上述操作或考虑您对使用 .Net Process 类等的建议。 您如何确定特定进程的#number 是多少?它不是进程的会话 ID。就像... w3wp.exe 1 如何知道它是 1,而另一个实例如何知道它是 #2? Windows 如何对它们重新排序?当#1 退出时,#2 是否刚刚开始写入#1 的数据?还是计数器“重命名”自己?这似乎是一个糟糕的设计,并且试图将进程与其自己的性能计数器相关联是不可能的混乱。

以上是关于即使进程没有退出,进程的性能计数器实例名称是不是可以更改的主要内容,如果未能解决你的问题,请参考以下文章

PDH 性能计数器实例名称

系统性能计数器的实例名称是不是已本地化?

Linux常用性能工具功能用法及原理

Python-网络编程之线程与进程

获取 C 中的进程性能计数器

Windows性能计数器配置应用