性能计数器实例名称与进程名称

Posted

技术标签:

【中文标题】性能计数器实例名称与进程名称【英文标题】:Performance Counter Instance Name vs. Process Name 【发布时间】:2015-04-28 14:57:54 【问题描述】:

我正在连接到 Process 类别中的各种性能计数器。我正在使用以下 c# 方法来确定获取计数器时要使用的 实例名称

private const string _categoryName = "Process";
private const string _processIdCounter = "ID Process";

public static bool TryGetInstanceName(Process process, out string instanceName)

    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string[] instanceNames = processCategory.GetInstanceNames();
    foreach (string name in instanceNames)
    
        using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
        
            if (process.Id == (int)processIdCounter.RawValue)
            
                instanceName = name;
                return true;
            
        
    

    instanceName = null;
    return false;

现在,我注意到返回的实例名称通常与 Process.ProcessName 的值匹配。

实例名和进程名有什么关系?

我问是因为我想简化例程中的foreach 循环,这样我就不必为无法匹配当前进程的实例获取ID Process 计数器。我设想的最终方法可能如下所示:

public static bool TryGetInstanceName(Process process, out string instanceName)

    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string[] instanceNames = processCategory.GetInstanceNames();
    foreach (string name in instanceNames)
    
        if (name /* more or less matches */ process.ProcessName)
        
            using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
            
                // ...
            
        
    

    instanceName = null;
    return false;

【问题讨论】:

【参考方案1】:

看到没有给出答案,我进行了更多的试错测试并观察到以下行为:

常规流程

看起来,对于第一个具有给定名称的常规进程,进程名称与实例名称匹配。后续同名进程,修改实例名,追加#1,#2,...

令人担忧的是,与进程关联的实例名称似乎也有可能发生变化。当数字序列中较早的进程结束时,这似乎会发生。在确定实例名称和获取相关计数器之间存在竞争条件!

服务流程

在服务控制管理器下运行的 Windows NT 服务的行为方式似乎与常规进程的行为方式相同。如果您在数字序列中较早结束服务进程,实例名称也会发生变化。

ASP.NET 应用程序

除了进程名称为w3wp 之外,同样的假设适用于托管在 IIS 下的应用程序。不同的应用程序。池肯定会获得不同的进程,并且通过启动和停止应用程序。池,我确定实例名称在与上述相同的情况下以相同的方式更改。

结论

我的结论是,实例名总是以进程名开头,方法可以修改如下:

public static bool TryGetInstanceName(Process process, out string instanceName)

    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string[] instanceNames = processCategory.GetInstanceNames();
    foreach (string name in instanceNames)
    
        if (name.StartsWith(process.ProcessName))
        
            using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
            
                if (process.Id == (int)processIdCounter.RawValue)
                
                    instanceName = name;
                    return true;
                
            
        
    

    instanceName = null;
    return false;

此外,当使用返回的实例名称时,确认上述竞争条件的存在非常重要。

(在没有进一步输入的情况下,我将接受此作为答案。请随时纠正我。)

【讨论】:

我正是在寻找这个 :D 通过registry change,您可以将windows配置为使用ProcessName_PID形式而不是附加#1等,这显然避免了horrible behaviour of a process' instance name changing。 创建 new 托管 PerformanceCounter 确实很慢,但此基础/信息可用于通过一些修改来制作缓存刷新方法。【参考方案2】:

这个解决方案会不会快一点:

public static bool TryGetInstanceName(Process process, out string instanceName)

    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string processName = Path.GetFileNameWithoutExtension(process.ProcessName);

    string[] instanceNames = processCategory.GetInstanceNames()
            .Where(inst => inst.StartsWith(processName))
            .ToArray();
    foreach (string name in instanceNames)
    
            using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
            
                if (process.Id == (int)processIdCounter.RawValue)
                
                    instanceName = name;
                    return true;
                
            
    

    instanceName = null;
    return false;

(从Rick Strahl's blog复制并稍作修改)。

但是,您需要注意:如果存在多个同名进程并且其中一个退出,则所有进程的命名都会更改:

与 Windows 进程实例名称有关的一件事是,当其中一个进程退出时,它们会动态变化。

例如,如果 chrome#8 退出,则 chrome#9 将变为 chrome#8,而 chrome#10 >将变为 chrome#9。此时,获取先前为 chrome#10 创建的计数器的值将引发异常。这 如果你想监控多个实例真的很烦人 多个进程,因为它开始监视进程退出和 重新创建所有计数器(真的很难看)。

一种方法是更改​​流程实例名称的生成方式>(请参阅http://support.microsoft.com/kb/281884)但这有 使用 perfmon api 影响其他应用程序的可能性。

【讨论】:

我已经有很长时间没有研究这个代码了,但我的直觉是这个解决方案会不太有效。首先,您在整个数组上运行 Where 谓词,然后使用 ToArray() 创建另一个数组,这完全没有必要。在我的解决方案中,不需要创建另一个数组、复制内存中的内容或在干草堆中找到针后运行谓词。 您可能对ToArray() 是正确的,但为什么Where 的效率低于获取所有实例并对其进行迭代?我的意思是通过 LINQ 进行优化的搜索在哪里,这意味着它很快,不是吗? 哦,等等...我们现在谈论的是速度还是 RAM 使用率?因为我猜我的代码会产生更多的 RAM 使用量(使用数组),但它的速度真的低效吗,比如说我在应用程序中使用的(后台)进程?【参考方案3】:

另外请注意,如果您正在监视同一进程的多个实例,则实例命名在不同类别之间是不一致的。因此,上面给出的解决方案仅在您从与 pid 被拉出的同一类别中提取计数器时才有效。我确实发现 pid 属于其他一些类别 - 但不是全部 - 并且命名不一致。

【讨论】:

哇,性能计数器真是一团糟。

以上是关于性能计数器实例名称与进程名称的主要内容,如果未能解决你的问题,请参考以下文章

PDH 性能计数器实例名称

服务模型性能计数器实例名称

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

OMS 逻辑磁盘性能计数器不存在磁盘实例名称

如何读取 Windows 性能计数器?

如何使用名称中带有 * 的 Get-Counter 选择性能计数器