Linux系统下用C#获取当前CPU使用率的技术问题求助
解决Linux下总CPU使用率计算问题
最优方案:直接读取/proc/stat文件
Linux系统的CPU统计信息原生存储在/proc/stat中,这是最高效、无权限问题的获取方式,完全避开Docker进程的权限限制。
原理
/proc/stat的第一行cpu记录了所有逻辑CPU的累计时间(单位:jiffies,通常1jiffy=1/100秒),包含用户态、内核态、空闲等多个时间字段。总CPU使用率通过两次读取的时间差值计算:
总CPU时间 = user + nice + system + idle + iowait + irq + softirq + ... 空闲时间差 = 当前idle - 上次idle 总时间差 = 当前总CPU时间 - 上次总CPU时间 使用率 = (1 - 空闲时间差 / 总时间差) * 100
C#实现代码
using System; using System.IO; using System.Threading.Tasks; public class CpuUsageCalculator { private (ulong total, ulong idle) _prevCpuStats; private readonly int _cpuCount = Environment.ProcessorCount; // 获取逻辑CPU核心数 public CpuUsageCalculator() { // 初始化首次统计数据 _prevCpuStats = ReadCpuStats(); } public double GetTotalCpuUsage() { var currentStats = ReadCpuStats(); var totalDiff = currentStats.total - _prevCpuStats.total; var idleDiff = currentStats.idle - _prevCpuStats.idle; if (totalDiff == 0) return 0; var usage = (1 - (double)idleDiff / totalDiff) * 100; _prevCpuStats = currentStats; return Math.Round(usage, 2); } private (ulong total, ulong idle) ReadCpuStats() { var statLine = File.ReadAllLines("/proc/stat")[0]; var parts = statLine.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); ulong total = 0; ulong idle = 0; // parts[0]是"cpu",从parts[1]开始是各时间字段 for (int i = 1; i < parts.Length; i++) { if (!ulong.TryParse(parts[i], out var value)) continue; total += value; if (i == 4) // 第5个字段是idle(索引从1开始) idle = value; } return (total, idle); } } // 使用示例:每10秒上报一次 public class UsageReporter { private readonly CpuUsageCalculator _cpuCalc = new CpuUsageCalculator(); public async Task StartReporting() { while (true) { await Task.Delay(10000); var cpuUsage = _cpuCalc.GetTotalCpuUsage(); Console.WriteLine($"当前总CPU使用率:{cpuUsage}%"); // 在这里执行上报逻辑 } } }
修正你第一种方法的问题
你用node_exporter数据计算时数值异常,是因为两个错误:
- 公式逻辑反了:你计算的是空闲率的倍数,而非使用率
- 未除以逻辑CPU核心数:
node_cpu_seconds_total是每个逻辑核心的时间总和,总可用时间应为delta.TotalSeconds * 逻辑核心数
修正后的代码:
// 先获取逻辑CPU核心数 int cpuCount = Environment.ProcessorCount; TimeSpan delta = DateTime.Now - _lastTimeBuildReport; var idleCurrent = currentExporterDic["node_cpu_seconds_total"].GetSum("idle"); var idlePrev = _previousExporterDic["node_cpu_seconds_total"].GetSum("idle"); var idleDiff = idleCurrent - idlePrev; // 计算实际使用率 double cpuLoad = 100 * (1 - idleDiff / (cpuCount * delta.TotalSeconds)); // 确保数值在0-100之间 cpuLoad = Math.Clamp(cpuLoad, 0, 100);
为什么第二种方法不可行
你用Process.GetProcesses()遍历所有进程的方式有致命问题:
- 无法访问Docker容器内的特权进程,必然抛出权限错误
- 效率极低:每个进程都要等待
totalMsPassed时间,遍历所有进程会导致耗时成倍增加 - 进程可能在遍历过程中退出,引发异常
这种方法完全不适用于Linux环境下的系统总CPU使用率统计。
内容的提问来源于stack exchange,提问作者Yuki1112
相关产品推荐
相关产品推荐

