package main import ( "bufio" "errors" "os" "strconv" "strings" "time" ) // readCPUUsage 读取CPU整体使用率 func readCPUUsage() (float64, error) { idle1, total1, err := readCPUTicks() if err != nil { return 0, err } time.Sleep(250 * time.Millisecond) idle2, total2, err := readCPUTicks() if err != nil { return 0, err } if total2 == total1 { return 0, errors.New("cpu totals unchanged") } idleDelta := float64(idle2 - idle1) totalDelta := float64(total2 - total1) usage := (1.0 - idleDelta/totalDelta) * 100 if usage < 0 { usage = 0 } return usage, nil } // readCPUTicks 读取CPU的idle和total ticks func readCPUTicks() (idle, total uint64, err error) { f, err := os.Open("/proc/stat") if err != nil { return 0, 0, err } defer f.Close() scanner := bufio.NewScanner(f) if !scanner.Scan() { return 0, 0, errors.New("failed to scan /proc/stat") } fields := strings.Fields(scanner.Text()) if len(fields) < 5 { return 0, 0, errors.New("unexpected /proc/stat format") } // fields[0] is "cpu" var vals []uint64 for _, f := range fields[1:] { v, err := strconv.ParseUint(f, 10, 64) if err != nil { return 0, 0, err } vals = append(vals, v) } for _, v := range vals { total += v } if len(vals) > 3 { idle = vals[3] // idle time // 注意:不包含 iowait,因为 iowait 不算真正的空闲时间 // 如果系统有 iowait,它会在 total 中,但不应该算作 idle } return idle, total, nil } // readPerCoreUsage 读取每个CPU核心的使用率 func readPerCoreUsage() []CoreUsage { coreUsages := []CoreUsage{} // 第一次读取 cores1 := readPerCoreTicks() time.Sleep(100 * time.Millisecond) // 减少到100ms // 第二次读取 cores2 := readPerCoreTicks() for i := 0; i < len(cores1) && i < len(cores2); i++ { idle1, total1 := cores1[i][0], cores1[i][1] idle2, total2 := cores2[i][0], cores2[i][1] if total2 == total1 { continue } idleDelta := float64(idle2 - idle1) totalDelta := float64(total2 - total1) usage := (1.0 - idleDelta/totalDelta) * 100 if usage < 0 { usage = 0 } coreUsages = append(coreUsages, CoreUsage{ Core: i, Percent: round(usage, 1), }) } return coreUsages } // readPerCoreTicks 读取每个CPU核心的ticks func readPerCoreTicks() [][2]uint64 { var result [][2]uint64 f, err := os.Open("/proc/stat") if err != nil { return result } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if !strings.HasPrefix(line, "cpu") { continue } if strings.HasPrefix(line, "cpu ") { continue // 跳过总的cpu行 } fields := strings.Fields(line) if len(fields) < 5 { continue } var vals []uint64 for _, f := range fields[1:] { v, err := strconv.ParseUint(f, 10, 64) if err != nil { break } vals = append(vals, v) } if len(vals) < 5 { continue } var total, idle uint64 for _, v := range vals { total += v } idle = vals[3] // idle time only, not including iowait result = append(result, [2]uint64{idle, total}) } return result } // readCPUTemperature 读取CPU温度 func readCPUTemperature() float64 { // 尝试从常见位置读取温度 paths := []string{ "/sys/class/thermal/thermal_zone0/temp", "/sys/class/hwmon/hwmon0/temp1_input", "/sys/class/hwmon/hwmon1/temp1_input", } for _, path := range paths { if temp := readTempFromFile(path); temp > 0 { return temp } } return 0 } // readLoadAverages 读取系统负载平均值 func readLoadAverages() []float64 { line := readFirstLine("/proc/loadavg") fields := strings.Fields(line) res := make([]float64, 0, 3) for i := 0; i < len(fields) && i < 3; i++ { v, err := strconv.ParseFloat(fields[i], 64) if err == nil { res = append(res, v) } } return res }