/** * system_monitor.cpp - 系统资源监控实现 * fix24 v24: 读取 /proc, /sys, statvfs 获取系统信息 */ #include "system_monitor.h" #include "common.h" #include #include #include #include #include #include #include #include #include #include namespace sysmon { // ========== CPU 使用率(两次采样差值) ========== double get_cpu_usage(int sample_ms) { // 读取 /proc/stat 两次,计算差值 auto read_cpu_times = []() -> std::vector { std::ifstream f("/proc/stat"); std::string line; std::getline(f, line); // "cpu user nice system idle iowait irq softirq steal" std::istringstream iss(line); std::string cpu_label; iss >> cpu_label; std::vector times; long long val; while (iss >> val) times.push_back(val); return times; }; auto t1 = read_cpu_times(); usleep(sample_ms * 1000); auto t2 = read_cpu_times(); if (t1.size() < 4 || t2.size() < 4) return 0.0; // 补齐到相同长度 while (t1.size() < t2.size()) t1.push_back(0); while (t2.size() < t1.size()) t2.push_back(0); long long total1 = 0, total2 = 0, idle1 = 0, idle2 = 0; for (size_t i = 0; i < t1.size(); i++) total1 += t1[i]; for (size_t i = 0; i < t2.size(); i++) total2 += t2[i]; // idle = index 3 (idle) + index 4 (iowait) if present idle1 = t1[3]; if (t1.size() > 4) idle1 += t1[4]; idle2 = t2[3]; if (t2.size() > 4) idle2 += t2[4]; long long total_diff = total2 - total1; long long idle_diff = idle2 - idle1; if (total_diff <= 0) return 0.0; return (double)(total_diff - idle_diff) / total_diff * 100.0; } // ========== CPU 温度 ========== double get_cpu_temperature() { // 尝试多个温度传感器路径(兼容 RPi4/Pi5、Ubuntu、Debian 等) const char* paths[] = { "/sys/class/thermal/thermal_zone0/temp", "/sys/class/thermal/thermal_zone1/temp", "/sys/class/hwmon/hwmon0/temp1_input", "/sys/class/hwmon/hwmon1/temp1_input", "/sys/class/hwmon/hwmon2/temp1_input", "/sys/devices/virtual/thermal/thermal_zone0/temp", "/sys/devices/platform/scb/fd5d0000.tmp/hwmon/hwmon0/temp1_input", nullptr }; for (int i = 0; paths[i]; i++) { std::ifstream f(paths[i]); if (f.is_open()) { long long millideg = 0; f >> millideg; // 合理温度范围:-40°C ~ 125°C(原始值为毫度) if (millideg > -40000 && millideg < 125000) { return millideg / 1000.0; } // 如果值在合理范围但 > 1000,可能是已经以°C为单位 if (millideg > 1000 && millideg < 125) { return millideg; } } } // 尝试用 vcgencmd(仅 Raspberry Pi OS) { FILE *fp = popen("vcgencmd measure_temp 2>/dev/null", "r"); if (fp) { char buf[128] = {0}; if (fgets(buf, sizeof(buf) - 1, fp)) { // 格式: temp=42.8'C char *eq = strchr(buf, '='); if (eq) { double temp = atof(eq + 1); if (temp > 0 && temp < 125) { pclose(fp); return temp; } } } pclose(fp); } } return -1.0; // 温度不可用 } // ========== 内存信息 ========== void get_memory_info(long &total_mb, long &used_mb, long &avail_mb, double &percent) { total_mb = used_mb = avail_mb = 0; percent = 0.0; std::ifstream f("/proc/meminfo"); if (!f.is_open()) return; long mem_total = 0, mem_free = 0, mem_available = 0, buffers = 0, cached = 0; std::string line; while (std::getline(f, line)) { if (line.find("MemTotal:") == 0) sscanf(line.c_str(), "MemTotal: %ld kB", &mem_total); else if (line.find("MemFree:") == 0) sscanf(line.c_str(), "MemFree: %ld kB", &mem_free); else if (line.find("MemAvailable:") == 0) sscanf(line.c_str(), "MemAvailable: %ld kB", &mem_available); else if (line.find("Buffers:") == 0) sscanf(line.c_str(), "Buffers: %ld kB", &buffers); else if (line.find("Cached:") == 0 && line.find("SwapCached") == std::string::npos) sscanf(line.c_str(), "Cached: %ld kB", &cached); } total_mb = mem_total / 1024; avail_mb = mem_available / 1024; used_mb = total_mb - avail_mb; if (total_mb > 0) { percent = (double)used_mb / total_mb * 100.0; } } // ========== 磁盘信息 ========== void get_disk_info(const std::string &path, long &total_gb, long &used_gb, long &avail_gb, double &percent) { total_gb = used_gb = avail_gb = 0; percent = 0.0; struct statvfs stat; if (statvfs(path.c_str(), &stat) != 0) return; long long total = (long long)stat.f_blocks * stat.f_frsize; long long avail = (long long)stat.f_bavail * stat.f_frsize; long long used = total - ((long long)stat.f_bfree * stat.f_frsize); total_gb = total / (1024LL * 1024 * 1024); used_gb = used / (1024LL * 1024 * 1024); avail_gb = avail / (1024LL * 1024 * 1024); if (total > 0) { percent = (double)used / total * 100.0; } } // ========== 系统启动时间 ========== std::string get_uptime_str(long &seconds) { struct sysinfo si; if (sysinfo(&si) != 0) { seconds = 0; return "unknown"; } seconds = si.uptime; long days = seconds / 86400; long hours = (seconds % 86400) / 3600; long mins = (seconds % 3600) / 60; std::ostringstream oss; if (days > 0) oss << days << "天"; oss << hours << "小时" << mins << "分钟"; return oss.str(); } // ========== 负载均值 ========== void get_load_avg(double &load1, double &load5, double &load15) { double loads[3] = {0, 0, 0}; if (getloadavg(loads, 3) != -1) { load1 = loads[0]; load5 = loads[1]; load15 = loads[2]; } else { load1 = load5 = load15 = 0; } } // ========== 进程资源占用 ========== void get_process_resource(int pid, long &rss_mb, double &cpu_percent) { rss_mb = 0; cpu_percent = 0.0; char path[64]; snprintf(path, sizeof(path), "/proc/%d/statm", pid); std::ifstream f(path); if (!f.is_open()) return; long pages = 0; f >> pages; // 第一个字段是 total program size f >> pages; // 第二个字段是 resident set size rss_mb = pages * (sysconf(_SC_PAGESIZE) / 1024) / 1024; } // ========== 格式化文件大小 ========== std::string format_size(long mb) { if (mb >= 1024) { char buf[32]; snprintf(buf, sizeof(buf), "%.1f GB", mb / 1024.0); return buf; } return std::to_string(mb) + " MB"; } // ========== CPU 核心数 ========== static int get_cpu_core_count() { return sysconf(_SC_NPROCESSORS_ONLN); } // ========== 完整系统状态快照 ========== SystemStatus get_system_status() { SystemStatus s{}; // ✅ fix24-v39: 使用值初始化替代 memset,避免破坏 std::string/vector 内部状态 // CPU s.cpu_core_count = get_cpu_core_count(); s.cpu_usage_percent = get_cpu_usage(100); get_load_avg(s.cpu_load_1min, s.cpu_load_5min, s.cpu_load_15min); // 内存 get_memory_info(s.mem_total_mb, s.mem_used_mb, s.mem_available_mb, s.mem_usage_percent); // 温度 s.cpu_temp = get_cpu_temperature(); s.temp_available = (s.cpu_temp > -50.0 && s.cpu_temp < 150.0); // ✅ fix24-v39: 放宽判断,-50~150范围内均视为有效 // 磁盘(根分区) get_disk_info("/", s.disk_total_gb, s.disk_used_gb, s.disk_available_gb, s.disk_usage_percent); // 系统信息 char hostname_buf[256] = {0}; gethostname(hostname_buf, sizeof(hostname_buf) - 1); s.hostname = hostname_buf; // 内核版本 { std::ifstream f("/proc/version"); if (f.is_open()) { std::string line; std::getline(f, line); // 提取 "Linux version X.Y.Z-..." 部分 size_t pos = line.find("version "); if (pos != std::string::npos) { pos += 8; size_t end = line.find(' ', pos); if (end != std::string::npos) { s.kernel_version = line.substr(pos, end - pos); } else { s.kernel_version = line.substr(pos, 20); } } } } // 启动时间 s.uptime_string = get_uptime_str(s.uptime_seconds); // 进程信息 s.plate_rec_pid = getpid(); get_process_resource(s.plate_rec_pid, s.plate_rec_rss_mb, s.plate_rec_cpu_percent); return s; } } // namespace sysmon