Ubuntu 20.04下C++获取CPU使用率异常,请求排查问题
CPU使用率读取错误排查与修复
问题现象
在Ubuntu 20.04系统中读取CPU使用率时,始终得到固定错误结果:
Utilization cpu = 0
Utilization core 8 = 100
核心错误分析
1. 数据读取逻辑错误
getPercentageCpu函数存在严重逻辑问题:
- 在
getCPUUsage和Core::getCPUUsage中,已经通过getline读取了目标CPU/core的行数据到str参数中 - 但
getPercentageCpu内部又执行了cpu_stat.ignore(5, ' ')和getline(cpu_stat, str),直接丢弃传入的有效数据,转而读取流中的下一行(甚至可能是不存在的行),导致解析内容完全错误
2. 使用率计算逻辑错误
Linux的/proc/stat文件记录的是系统启动以来的累计CPU时间,并非瞬时使用率。仅读取一次数据计算出的是开机到当前的平均使用率,而非实时使用率,必须读取两次数据(间隔一段时间),通过差值计算才能得到真实的瞬时使用率。
3. 字段解析硬编码问题
代码中固定循环9次解析字段,但/proc/stat的CPU行可能包含更多字段(如guest、guest_nice等),硬编码次数会导致解析不完整或字符串越界访问。
4. Core编号检查缺失
未验证目标core编号是否存在,若传入的编号超出系统实际核心数,会读取到错误的行数据。
修复方案与代码示例
第一步:重构CPU行解析函数
改为动态解析传入的行数据,提取累计时间:
#include <sstream> #include <string> #include <utility> // 解析/proc/stat中的单条CPU记录,返回(idle累计时间, 总累计时间) std::pair<uint64_t, uint64_t> parseCpuLine(const std::string& line) { uint64_t idleTime = 0; uint64_t totalTime = 0; std::istringstream lineStream(line); std::string prefix; lineStream >> prefix; // 跳过"cpu"或"cpuX"前缀 uint64_t timeValue; int fieldIndex = 0; while (lineStream >> timeValue) { totalTime += timeValue; // idle是第4个字段(索引从0开始) if (fieldIndex == 3) { idleTime = timeValue; } fieldIndex++; } return {idleTime, totalTime}; }
第二步:修复总CPU使用率读取函数
改为读取两次数据并计算差值:
#include <fstream> #include <iostream> #include <unistd.h> #include <cmath> uint8_t CPU::getCPUUsage() const { std::ifstream statFile("/proc/stat"); if (!statFile.is_open()) { std::cerr << "无法打开/proc/stat\n"; return -1; } std::string line; std::getline(statFile, line); auto [idle1, total1] = parseCpuLine(line); statFile.close(); usleep(100000); // 间隔100ms,可根据需求调整 statFile.open("/proc/stat"); std::getline(statFile, line); auto [idle2, total2] = parseCpuLine(line); statFile.close(); uint64_t deltaIdle = idle2 - idle1; uint64_t deltaTotal = total2 - total1; if (deltaTotal == 0) { return 0; } float utilization = (1.0f - static_cast<float>(deltaIdle) / deltaTotal) * 100; return static_cast<uint8_t>(std::round(utilization)); }
第三步:修复单Core使用率读取函数
增加Core存在性检查,同样采用两次读取差值计算:
uint8_t Core::getCPUUsage(uint8_t CPUNumber) const { std::string targetPrefix = "cpu" + std::to_string(CPUNumber); // 第一次读取累计时间 std::ifstream statFile("/proc/stat"); if (!statFile.is_open()) { std::cerr << "无法打开/proc/stat\n"; return -1; } std::string line; bool coreFound = false; while (std::getline(statFile, line)) { if (line.substr(0, targetPrefix.size()) == targetPrefix) { coreFound = true; break; } } statFile.close(); if (!coreFound) { std::cerr << "核心" << static_cast<int>(CPUNumber) << "不存在\n"; return -1; } auto [idle1, total1] = parseCpuLine(line); usleep(100000); // 第二次读取累计时间 statFile.open("/proc/stat"); coreFound = false; while (std::getline(statFile, line)) { if (line.substr(0, targetPrefix.size()) == targetPrefix) { coreFound = true; break; } } statFile.close(); if (!coreFound) { std::cerr << "核心" << static_cast<int>(CPUNumber) << "不存在\n"; return -1; } auto [idle2, total2] = parseCpuLine(line); uint64_t deltaIdle = idle2 - idle1; uint64_t deltaTotal = total2 - total1; if (deltaTotal == 0) { return 0; } float utilization = (1.0f - static_cast<float>(deltaIdle) / deltaTotal) * 100; return static_cast<uint8_t>(std::round(utilization)); }
第四步:调整主函数
由于读取函数内部已经包含间隔等待,可适当调整主循环的sleep时间:
#include "CPU.hpp" int main() { Core info_proc; CPU info_cpu; uint8_t core_utilization; uint8_t cpu_utilization; while (1) { core_utilization = info_proc.getCPUUsage(8); cpu_utilization = info_cpu.getCPUUsage(); std::cout << "Utilization cpu = " << static_cast<int>(cpu_utilization) << "\n"; std::cout << "Utilization core 8 = " << static_cast<int>(core_utilization) << "\n"; std::cout << "\033[H\033[J"; usleep(500000); // 调整为500ms,避免重复等待 } }
内容的提问来源于stack exchange,提问作者Tears4fears
相关产品推荐
相关产品推荐

