system_monitor.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /**
  2. * system_monitor.cpp - 系统资源监控实现
  3. * fix24 v24: 读取 /proc, /sys, statvfs 获取系统信息
  4. */
  5. #include "system_monitor.h"
  6. #include "common.h"
  7. #include <fstream>
  8. #include <sstream>
  9. #include <cstring>
  10. #include <cstdlib>
  11. #include <cstdio>
  12. #include <unistd.h>
  13. #include <sys/statvfs.h>
  14. #include <sys/sysinfo.h>
  15. #include <dirent.h>
  16. #include <algorithm>
  17. namespace sysmon {
  18. // ========== CPU 使用率(两次采样差值) ==========
  19. double get_cpu_usage(int sample_ms) {
  20. // 读取 /proc/stat 两次,计算差值
  21. auto read_cpu_times = []() -> std::vector<long long> {
  22. std::ifstream f("/proc/stat");
  23. std::string line;
  24. std::getline(f, line); // "cpu user nice system idle iowait irq softirq steal"
  25. std::istringstream iss(line);
  26. std::string cpu_label;
  27. iss >> cpu_label;
  28. std::vector<long long> times;
  29. long long val;
  30. while (iss >> val) times.push_back(val);
  31. return times;
  32. };
  33. auto t1 = read_cpu_times();
  34. usleep(sample_ms * 1000);
  35. auto t2 = read_cpu_times();
  36. if (t1.size() < 4 || t2.size() < 4) return 0.0;
  37. // 补齐到相同长度
  38. while (t1.size() < t2.size()) t1.push_back(0);
  39. while (t2.size() < t1.size()) t2.push_back(0);
  40. long long total1 = 0, total2 = 0, idle1 = 0, idle2 = 0;
  41. for (size_t i = 0; i < t1.size(); i++) total1 += t1[i];
  42. for (size_t i = 0; i < t2.size(); i++) total2 += t2[i];
  43. // idle = index 3 (idle) + index 4 (iowait) if present
  44. idle1 = t1[3];
  45. if (t1.size() > 4) idle1 += t1[4];
  46. idle2 = t2[3];
  47. if (t2.size() > 4) idle2 += t2[4];
  48. long long total_diff = total2 - total1;
  49. long long idle_diff = idle2 - idle1;
  50. if (total_diff <= 0) return 0.0;
  51. return (double)(total_diff - idle_diff) / total_diff * 100.0;
  52. }
  53. // ========== CPU 温度 ==========
  54. double get_cpu_temperature() {
  55. // 尝试多个温度传感器路径(兼容 RPi4/Pi5、Ubuntu、Debian 等)
  56. const char* paths[] = {
  57. "/sys/class/thermal/thermal_zone0/temp",
  58. "/sys/class/thermal/thermal_zone1/temp",
  59. "/sys/class/hwmon/hwmon0/temp1_input",
  60. "/sys/class/hwmon/hwmon1/temp1_input",
  61. "/sys/class/hwmon/hwmon2/temp1_input",
  62. "/sys/devices/virtual/thermal/thermal_zone0/temp",
  63. "/sys/devices/platform/scb/fd5d0000.tmp/hwmon/hwmon0/temp1_input",
  64. nullptr
  65. };
  66. for (int i = 0; paths[i]; i++) {
  67. std::ifstream f(paths[i]);
  68. if (f.is_open()) {
  69. long long millideg = 0;
  70. f >> millideg;
  71. // 合理温度范围:-40°C ~ 125°C(原始值为毫度)
  72. if (millideg > -40000 && millideg < 125000) {
  73. return millideg / 1000.0;
  74. }
  75. // 如果值在合理范围但 > 1000,可能是已经以°C为单位
  76. if (millideg > 1000 && millideg < 125) {
  77. return millideg;
  78. }
  79. }
  80. }
  81. // 尝试用 vcgencmd(仅 Raspberry Pi OS)
  82. {
  83. FILE *fp = popen("vcgencmd measure_temp 2>/dev/null", "r");
  84. if (fp) {
  85. char buf[128] = {0};
  86. if (fgets(buf, sizeof(buf) - 1, fp)) {
  87. // 格式: temp=42.8'C
  88. char *eq = strchr(buf, '=');
  89. if (eq) {
  90. double temp = atof(eq + 1);
  91. if (temp > 0 && temp < 125) {
  92. pclose(fp);
  93. return temp;
  94. }
  95. }
  96. }
  97. pclose(fp);
  98. }
  99. }
  100. return -1.0; // 温度不可用
  101. }
  102. // ========== 内存信息 ==========
  103. void get_memory_info(long &total_mb, long &used_mb, long &avail_mb, double &percent) {
  104. total_mb = used_mb = avail_mb = 0;
  105. percent = 0.0;
  106. std::ifstream f("/proc/meminfo");
  107. if (!f.is_open()) return;
  108. long mem_total = 0, mem_free = 0, mem_available = 0, buffers = 0, cached = 0;
  109. std::string line;
  110. while (std::getline(f, line)) {
  111. if (line.find("MemTotal:") == 0) sscanf(line.c_str(), "MemTotal: %ld kB", &mem_total);
  112. else if (line.find("MemFree:") == 0) sscanf(line.c_str(), "MemFree: %ld kB", &mem_free);
  113. else if (line.find("MemAvailable:") == 0) sscanf(line.c_str(), "MemAvailable: %ld kB", &mem_available);
  114. else if (line.find("Buffers:") == 0) sscanf(line.c_str(), "Buffers: %ld kB", &buffers);
  115. else if (line.find("Cached:") == 0 && line.find("SwapCached") == std::string::npos)
  116. sscanf(line.c_str(), "Cached: %ld kB", &cached);
  117. }
  118. total_mb = mem_total / 1024;
  119. avail_mb = mem_available / 1024;
  120. used_mb = total_mb - avail_mb;
  121. if (total_mb > 0) {
  122. percent = (double)used_mb / total_mb * 100.0;
  123. }
  124. }
  125. // ========== 磁盘信息 ==========
  126. void get_disk_info(const std::string &path, long &total_gb, long &used_gb, long &avail_gb, double &percent) {
  127. total_gb = used_gb = avail_gb = 0;
  128. percent = 0.0;
  129. struct statvfs stat;
  130. if (statvfs(path.c_str(), &stat) != 0) return;
  131. long long total = (long long)stat.f_blocks * stat.f_frsize;
  132. long long avail = (long long)stat.f_bavail * stat.f_frsize;
  133. long long used = total - ((long long)stat.f_bfree * stat.f_frsize);
  134. total_gb = total / (1024LL * 1024 * 1024);
  135. used_gb = used / (1024LL * 1024 * 1024);
  136. avail_gb = avail / (1024LL * 1024 * 1024);
  137. if (total > 0) {
  138. percent = (double)used / total * 100.0;
  139. }
  140. }
  141. // ========== 系统启动时间 ==========
  142. std::string get_uptime_str(long &seconds) {
  143. struct sysinfo si;
  144. if (sysinfo(&si) != 0) {
  145. seconds = 0;
  146. return "unknown";
  147. }
  148. seconds = si.uptime;
  149. long days = seconds / 86400;
  150. long hours = (seconds % 86400) / 3600;
  151. long mins = (seconds % 3600) / 60;
  152. std::ostringstream oss;
  153. if (days > 0) oss << days << "天";
  154. oss << hours << "小时" << mins << "分钟";
  155. return oss.str();
  156. }
  157. // ========== 负载均值 ==========
  158. void get_load_avg(double &load1, double &load5, double &load15) {
  159. double loads[3] = {0, 0, 0};
  160. if (getloadavg(loads, 3) != -1) {
  161. load1 = loads[0];
  162. load5 = loads[1];
  163. load15 = loads[2];
  164. } else {
  165. load1 = load5 = load15 = 0;
  166. }
  167. }
  168. // ========== 进程资源占用 ==========
  169. void get_process_resource(int pid, long &rss_mb, double &cpu_percent) {
  170. rss_mb = 0;
  171. cpu_percent = 0.0;
  172. char path[64];
  173. snprintf(path, sizeof(path), "/proc/%d/statm", pid);
  174. std::ifstream f(path);
  175. if (!f.is_open()) return;
  176. long pages = 0;
  177. f >> pages; // 第一个字段是 total program size
  178. f >> pages; // 第二个字段是 resident set size
  179. rss_mb = pages * (sysconf(_SC_PAGESIZE) / 1024) / 1024;
  180. }
  181. // ========== 格式化文件大小 ==========
  182. std::string format_size(long mb) {
  183. if (mb >= 1024) {
  184. char buf[32];
  185. snprintf(buf, sizeof(buf), "%.1f GB", mb / 1024.0);
  186. return buf;
  187. }
  188. return std::to_string(mb) + " MB";
  189. }
  190. // ========== CPU 核心数 ==========
  191. static int get_cpu_core_count() {
  192. return sysconf(_SC_NPROCESSORS_ONLN);
  193. }
  194. // ========== 完整系统状态快照 ==========
  195. SystemStatus get_system_status() {
  196. SystemStatus s{}; // ✅ fix24-v39: 使用值初始化替代 memset,避免破坏 std::string/vector 内部状态
  197. // CPU
  198. s.cpu_core_count = get_cpu_core_count();
  199. s.cpu_usage_percent = get_cpu_usage(100);
  200. get_load_avg(s.cpu_load_1min, s.cpu_load_5min, s.cpu_load_15min);
  201. // 内存
  202. get_memory_info(s.mem_total_mb, s.mem_used_mb, s.mem_available_mb, s.mem_usage_percent);
  203. // 温度
  204. s.cpu_temp = get_cpu_temperature();
  205. s.temp_available = (s.cpu_temp > -50.0 && s.cpu_temp < 150.0); // ✅ fix24-v39: 放宽判断,-50~150范围内均视为有效
  206. // 磁盘(根分区)
  207. get_disk_info("/", s.disk_total_gb, s.disk_used_gb, s.disk_available_gb, s.disk_usage_percent);
  208. // 系统信息
  209. char hostname_buf[256] = {0};
  210. gethostname(hostname_buf, sizeof(hostname_buf) - 1);
  211. s.hostname = hostname_buf;
  212. // 内核版本
  213. {
  214. std::ifstream f("/proc/version");
  215. if (f.is_open()) {
  216. std::string line;
  217. std::getline(f, line);
  218. // 提取 "Linux version X.Y.Z-..." 部分
  219. size_t pos = line.find("version ");
  220. if (pos != std::string::npos) {
  221. pos += 8;
  222. size_t end = line.find(' ', pos);
  223. if (end != std::string::npos) {
  224. s.kernel_version = line.substr(pos, end - pos);
  225. } else {
  226. s.kernel_version = line.substr(pos, 20);
  227. }
  228. }
  229. }
  230. }
  231. // 启动时间
  232. s.uptime_string = get_uptime_str(s.uptime_seconds);
  233. // 进程信息
  234. s.plate_rec_pid = getpid();
  235. get_process_resource(s.plate_rec_pid, s.plate_rec_rss_mb, s.plate_rec_cpu_percent);
  236. return s;
  237. }
  238. } // namespace sysmon