183 lines
3.7 KiB
Go
183 lines
3.7 KiB
Go
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
|
||
}
|