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

Performance Counter Instance Name vs. Process Name

我正在连接到 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, #2, ...

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

服务进程

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

ASP.NET 应用程序

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

结论

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

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;
}

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

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

这个方案会不会快一点:

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复制并稍作修改)。

不过,您需要注意:如果有多个同名进程,其中一个退出,则所有进程的命名都会发生变化:

One thing to mention related to windows process instance names is that they change dynamically when one of the processes exits.

For example if chrome#8 exits, chrome#9 will become chrome#8 and chrome#10 >will become chrome#9. At this point getting the value of the counter >previously created for chrome#10 will throw an exception. This is really annoying if you want to to monitor multiple instances of multiple processes as it gets down to monitoring process exits and recreating all the counters (really ugly).

One way would be to change the way process instance names are generated >(see http://support.microsoft.com/kb/281884) but this has the potential of affecting other apps using the perfmon api.

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