package main import ( "bufio" "context" "os" "os/exec" "strconv" "strings" "time" ) // readSystemStats 读取系统统计信息 func readSystemStats() SystemStats { stats := SystemStats{} // 读取进程数量 stats.ProcessCount = countProcesses() // 读取已安装包数量和包管理器类型 stats.PackageCount, stats.PackageManager = countPackages() // 读取系统温度 stats.Temperature = readSystemTemperature() // 读取磁盘读写速度 stats.DiskReadSpeed, stats.DiskWriteSpeed = readDiskSpeed() // 读取 Top 5 进程 stats.TopProcesses = readTopProcesses() // 读取系统日志 stats.SystemLogs = readSystemLogs(10) return stats } func countProcesses() int { entries, err := os.ReadDir("/proc") if err != nil { return 0 } count := 0 for _, entry := range entries { if entry.IsDir() { // 进程目录是数字命名的 if _, err := strconv.Atoi(entry.Name()); err == nil { count++ } } } return count } func countPackages() (int, string) { // 尝试不同的包管理器 // dpkg (Debian/Ubuntu) if _, err := exec.LookPath("dpkg"); err == nil { cmd := exec.Command("dpkg", "-l") out, err := cmd.Output() if err == nil { lines := strings.Split(string(out), "\n") count := 0 for _, line := range lines { if strings.HasPrefix(line, "ii ") { count++ } } return count, "dpkg (apt)" } } // rpm (RedHat/CentOS/Fedora) if _, err := exec.LookPath("rpm"); err == nil { cmd := exec.Command("rpm", "-qa") out, err := cmd.Output() if err == nil { lines := strings.Split(strings.TrimSpace(string(out)), "\n") return len(lines), "rpm (yum/dnf)" } } // pacman (Arch Linux) if _, err := exec.LookPath("pacman"); err == nil { cmd := exec.Command("pacman", "-Q") out, err := cmd.Output() if err == nil { lines := strings.Split(strings.TrimSpace(string(out)), "\n") return len(lines), "pacman" } } return 0, "unknown" } func readSystemTemperature() float64 { var cpuTemp float64 = 0 var fallbackTemp float64 = 0 // 1. 优先读取 thermal_zone (通常是 CPU 温度) thermalDir := "/sys/class/thermal" entries, err := os.ReadDir(thermalDir) if err == nil { for _, entry := range entries { if !strings.HasPrefix(entry.Name(), "thermal_zone") { continue } tempPath := thermalDir + "/" + entry.Name() + "/temp" if temp := readTempFromFile(tempPath); temp > 0 && temp > 20 && temp < 120 { // thermal_zone0 通常是 CPU if entry.Name() == "thermal_zone0" { cpuTemp = temp break } else if fallbackTemp == 0 { fallbackTemp = temp } } } } // 2. 扫描所有 hwmon 设备,查找 CPU 温度 hwmonDir := "/sys/class/hwmon" entries, err = os.ReadDir(hwmonDir) if err == nil { for _, entry := range entries { hwmonPath := hwmonDir + "/" + entry.Name() // 读取 name 文件,检查是否是 CPU 相关 namePath := hwmonPath + "/name" name := strings.ToLower(strings.TrimSpace(readFirstLine(namePath))) // 检查是否是 CPU 温度传感器 isCPU := strings.Contains(name, "cpu") || strings.Contains(name, "core") || strings.Contains(name, "k10temp") || strings.Contains(name, "coretemp") || strings.Contains(name, "zenpower") // 尝试读取 temp1_input (通常是 CPU) temp1Path := hwmonPath + "/temp1_input" if temp := readTempFromFile(temp1Path); temp > 0 && temp > 20 && temp < 120 { if isCPU { cpuTemp = temp break } else if fallbackTemp == 0 { fallbackTemp = temp } } // 也尝试 temp2_input temp2Path := hwmonPath + "/temp2_input" if temp := readTempFromFile(temp2Path); temp > 0 && temp > 20 && temp < 120 { if isCPU && cpuTemp == 0 { cpuTemp = temp } else if fallbackTemp == 0 { fallbackTemp = temp } } } } // 优先返回 CPU 温度,如果没有则返回其他温度 if cpuTemp > 0 { return cpuTemp } return fallbackTemp } // readDiskSpeed 读取磁盘瞬时读写速度 (MB/s) func readDiskSpeed() (float64, float64) { // 第一次读取 readSectors1, writeSectors1 := getDiskSectors() if readSectors1 == 0 && writeSectors1 == 0 { return 0, 0 } // 等待1秒 time.Sleep(1 * time.Second) // 第二次读取 readSectors2, writeSectors2 := getDiskSectors() // 计算差值(扇区数) readDiff := readSectors2 - readSectors1 writeDiff := writeSectors2 - writeSectors1 // 扇区大小通常是 512 字节,转换为 MB/s readSpeed := float64(readDiff) * 512 / 1024 / 1024 writeSpeed := float64(writeDiff) * 512 / 1024 / 1024 return round(readSpeed, 2), round(writeSpeed, 2) } func getDiskSectors() (uint64, uint64) { f, err := os.Open("/proc/diskstats") if err != nil { return 0, 0 } defer f.Close() scanner := bufio.NewScanner(f) var maxRead uint64 = 0 var mainDevice string // 第一次遍历:找到读写量最大的主磁盘(通常是系统盘) for scanner.Scan() { line := scanner.Text() fields := strings.Fields(line) if len(fields) < 14 { continue } deviceName := fields[2] // 跳过分区(分区名通常包含数字,如 sda1, vda1, nvme0n1p1) if strings.ContainsAny(deviceName, "0123456789") && !strings.HasPrefix(deviceName, "nvme") && !strings.HasPrefix(deviceName, "loop") { continue } // 跳过虚拟设备 if strings.HasPrefix(deviceName, "loop") || strings.HasPrefix(deviceName, "ram") || strings.HasPrefix(deviceName, "zram") { continue } readSectors, _ := strconv.ParseUint(fields[5], 10, 64) // 选择读写量最大的作为主磁盘 if readSectors > maxRead { maxRead = readSectors mainDevice = deviceName } } // 第二次遍历:读取主磁盘的数据 f.Close() f, err = os.Open("/proc/diskstats") if err != nil { return 0, 0 } scanner = bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() fields := strings.Fields(line) if len(fields) < 14 { continue } if fields[2] == mainDevice { readSectors, _ := strconv.ParseUint(fields[5], 10, 64) writeSectors, _ := strconv.ParseUint(fields[9], 10, 64) return readSectors, writeSectors } } // 如果没找到,尝试常见的设备名(向后兼容) f.Close() f, err = os.Open("/proc/diskstats") if err != nil { return 0, 0 } scanner = bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() fields := strings.Fields(line) if len(fields) < 14 { continue } deviceName := fields[2] if deviceName == "sda" || deviceName == "vda" || deviceName == "nvme0n1" { readSectors, _ := strconv.ParseUint(fields[5], 10, 64) writeSectors, _ := strconv.ParseUint(fields[9], 10, 64) return readSectors, writeSectors } } return 0, 0 } // readTopProcesses 读取 Top 5 进程 (按 CPU 使用率) func readTopProcesses() []ProcessInfo { processes := []ProcessInfo{} // 读取系统总内存 memInfo, _ := readMemory() totalMemGB := float64(memInfo.TotalBytes) / 1024 / 1024 / 1024 // 使用 ps 命令获取进程信息,添加超时控制 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "ps", "aux", "--sort=-%cpu", "--no-headers") out, err := cmd.Output() if err != nil { return processes } lines := strings.Split(string(out), "\n") count := 0 for _, line := range lines { if count >= 5 { // 只取前5个 break } line = strings.TrimSpace(line) if line == "" { continue } fields := strings.Fields(line) if len(fields) < 11 { continue } pid, _ := strconv.Atoi(fields[1]) cpu, _ := strconv.ParseFloat(fields[2], 64) mem, _ := strconv.ParseFloat(fields[3], 64) // 计算内存MB数 memoryMB := (mem / 100) * totalMemGB * 1024 // 命令可能包含空格,从第11个字段开始拼接 command := strings.Join(fields[10:], " ") if len(command) > 50 { command = command[:50] + "..." } processes = append(processes, ProcessInfo{ PID: pid, Name: fields[10], CPU: round(cpu, 1), Memory: round(mem, 1), MemoryMB: round(memoryMB, 1), Command: command, }) count++ } return processes } // readSystemLogs 读取系统最新日志 func readSystemLogs(count int) []string { logs := []string{} // 尝试使用 journalctl 读取系统日志 if _, err := exec.LookPath("journalctl"); err == nil { cmd := exec.Command("journalctl", "-n", strconv.Itoa(count), "--no-pager", "-o", "short") out, err := cmd.Output() if err == nil { lines := strings.Split(strings.TrimSpace(string(out)), "\n") for _, line := range lines { if line != "" { logs = append(logs, line) } } return logs } } // 如果 journalctl 不可用,尝试读取 /var/log/syslog 或 /var/log/messages logFiles := []string{"/var/log/syslog", "/var/log/messages"} for _, logFile := range logFiles { f, err := os.Open(logFile) if err != nil { continue } defer f.Close() // 读取最后几行 var lines []string scanner := bufio.NewScanner(f) for scanner.Scan() { lines = append(lines, scanner.Text()) } // 取最后count行 start := len(lines) - count if start < 0 { start = 0 } logs = lines[start:] break } return logs }