package main import ( "bufio" "os" "sort" "strconv" "strings" ) // readMemory 读取内存信息 func readMemory() (MemoryMetrics, error) { totals := map[string]uint64{} f, err := os.Open("/proc/meminfo") if err != nil { return MemoryMetrics{}, err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() parts := strings.Split(line, ":") if len(parts) < 2 { continue } key := strings.TrimSpace(parts[0]) fields := strings.Fields(strings.TrimSpace(parts[1])) if len(fields) == 0 { continue } value, err := strconv.ParseUint(fields[0], 10, 64) if err != nil { continue } totals[key] = value * 1024 // kB to bytes } total := totals["MemTotal"] // 优先使用 MemAvailable(Linux 3.14+),如果没有则计算 var free uint64 if available, ok := totals["MemAvailable"]; ok && available > 0 { free = available } else { // 回退到 MemFree + Buffers + Cached(适用于较老的系统) free = totals["MemFree"] if buffers, ok := totals["Buffers"]; ok { free += buffers } if cached, ok := totals["Cached"]; ok { free += cached } } used := total - free usedPercent := 0.0 if total > 0 { usedPercent = (float64(used) / float64(total)) * 100 } swapTotal := totals["SwapTotal"] swapFree := totals["SwapFree"] swapUsed := uint64(0) if swapTotal >= swapFree { swapUsed = swapTotal - swapFree } swapUsedPct := 0.0 if swapTotal > 0 { swapUsedPct = (float64(swapUsed) / float64(swapTotal)) * 100 } swapType := summarizeSwapTypes(swapTotal) return MemoryMetrics{ TotalBytes: total, UsedBytes: used, FreeBytes: free, UsedPercent: round(usedPercent, 2), SwapTotalBytes: swapTotal, SwapUsedBytes: swapUsed, SwapFreeBytes: swapFree, SwapUsedPercent: round(swapUsedPct, 2), SwapType: swapType, }, nil } // summarizeSwapTypes 根据 /proc/swaps 汇总交换区类型(file / partition / zram 等);无 swap 时为「无」 func summarizeSwapTypes(swapTotalFromMeminfo uint64) string { if swapTotalFromMeminfo == 0 { return "无" } f, err := os.Open("/proc/swaps") if err != nil { return "未知" } defer f.Close() scanner := bufio.NewScanner(f) lineNum := 0 seen := map[string]struct{}{} for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) lineNum++ if lineNum == 1 || line == "" { continue } fields := strings.Fields(line) if len(fields) < 2 { continue } filename := fields[0] typ := fields[1] if strings.Contains(strings.ToLower(filename), "zram") { typ = "zram" } seen[typ] = struct{}{} } if len(seen) == 0 { if swapTotalFromMeminfo > 0 { return "未知" } return "无" } list := make([]string, 0, len(seen)) for t := range seen { list = append(list, t) } sort.Strings(list) return strings.Join(list, " + ") }