From 7071fa746bd512e95a7c20c5ddca56e6d018949d Mon Sep 17 00:00:00 2001 From: Akizon77 Date: Sun, 30 Nov 2025 20:26:54 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20Linux=20=E4=B8=8B=20ntfs-3g=20?= =?UTF-8?q?=E5=88=86=E5=8C=BA=20(fuseblk)=20=E8=AF=86=E5=88=AB=E4=B8=BA?= =?UTF-8?q?=E8=99=9A=E6=8B=9F=E7=A3=81=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- monitoring/unit/disk.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/monitoring/unit/disk.go b/monitoring/unit/disk.go index f164859..423a9ed 100644 --- a/monitoring/unit/disk.go +++ b/monitoring/unit/disk.go @@ -85,6 +85,10 @@ func isPhysicalDisk(part disk.PartitionStat) bool { } fstype := strings.ToLower(part.Fstype) + // 针对 Linux 下通过 ntfs-3g 挂载的 NTFS 分区 (fuseblk),这是实际物理磁盘,不应排除 + if fstype == "fuseblk" { + return true + } var fstypeToExclude = []string{ "tmpfs", "devtmpfs", From a9d191d6c3ab4cf8ceaaba90e550fac61772ef28 Mon Sep 17 00:00:00 2001 From: Akizon77 Date: Sun, 30 Nov 2025 22:07:12 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20GPU=20?= =?UTF-8?q?=E8=AF=86=E5=88=AB=EF=BC=8C=E6=94=AF=E6=8C=81ARM=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E6=9D=BF=E5=92=8C=E6=89=8B=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- monitoring/unit/gpu_linux.go | 245 ++++++++++++++++++++++++++++++----- 1 file changed, 210 insertions(+), 35 deletions(-) diff --git a/monitoring/unit/gpu_linux.go b/monitoring/unit/gpu_linux.go index 6a2c2d5..6587868 100644 --- a/monitoring/unit/gpu_linux.go +++ b/monitoring/unit/gpu_linux.go @@ -4,52 +4,227 @@ package monitoring import ( + "bytes" + "os" "os/exec" + "path/filepath" + "regexp" "strings" ) func GpuName() string { - // 调整优先级:专用显卡厂商优先,避免只识别集成显卡 - accept := []string{"nvidia", "amd", "radeon", "vga", "3d"} + if name := getFromLspci(); name != "None" { + return name + } + + if name := getFromSysfsDRM(); name != "None" { + return name + } + return "None" +} + +func getFromLspci() string { out, err := exec.Command("lspci").Output() - if err == nil { - lines := strings.Split(string(out), "\n") - - // 首先尝试找专用显卡 - for _, line := range lines { - lower := strings.ToLower(line) - - // 跳过集成显卡和管理控制器 - if strings.Contains(lower, "aspeed") || - strings.Contains(lower, "matrox") || - strings.Contains(lower, "management") { - continue - } - - // 优先匹配专用显卡厂商 - for _, a := range accept { - if strings.Contains(lower, a) { - parts := strings.SplitN(line, ":", 4) - if len(parts) >= 4 { - return strings.TrimSpace(parts[3]) - } else if len(parts) == 3 { - return strings.TrimSpace(parts[2]) - } else if len(parts) == 2 { - return strings.TrimSpace(parts[1]) - } - } + if err != nil { + return "None" + } + excludePatterns := []string{ + "^1111", // 1111 (rev 02) + `(?i)^cirrus logic (cl[-\s]?)?gd 5`, // CL-GD 系列 1990 年代中期的产物, 现常用于虚拟机 + "(?i)virtio", + "(?i)vmware", + `(?i)qxl`, // SPICE 虚拟显卡 + `(?i)hyper-v`, + } + + lines := strings.Split(string(out), "\n") + + priorityVendors := []string{"nvidia", "amd", "radeon", "intel", "arc", "snap", "qualcomm", "snapdragon"} + + isExcluded := func(name string) bool { + for _, pattern := range excludePatterns { + if matched, _ := regexp.MatchString(pattern, name); matched { + return true } } - - // 如果没有找到专用显卡,返回第一个VGA设备作为兜底 - for _, line := range lines { - if strings.Contains(strings.ToLower(line), "vga") { - parts := strings.SplitN(line, ":", 4) - if len(parts) >= 3 { - return strings.TrimSpace(parts[2]) + return false + } + + extractName := func(line string) string { + // 取最后一个冒号之后的内容 + idx := strings.LastIndex(line, ":") + if idx == -1 || idx == len(line)-1 { + return "" + } + name := strings.TrimSpace(line[idx+1:]) + + // 去除末尾的 (rev xx) + if parenIdx := strings.LastIndex(name, "("); parenIdx != -1 { + name = strings.TrimSpace(name[:parenIdx]) + } + return name + } + + // 寻找 priorityVendors + for _, line := range lines { + lower := strings.ToLower(line) + + // 必须确认是显示设备,防止匹配到 Intel 网卡或 Qualcomm 蓝牙 + if !strings.Contains(lower, "vga") && !strings.Contains(lower, "3d") && !strings.Contains(lower, "display") { + continue + } + + for _, vendor := range priorityVendors { + if strings.Contains(lower, vendor) { + name := extractName(line) + if name != "" && !isExcluded(name) { + // 找到独显立刻返回 + return name } } } } + + // 任意非黑名单的 VGA 设备 + for _, line := range lines { + lower := strings.ToLower(line) + if strings.Contains(lower, "vga") || strings.Contains(lower, "3d") || strings.Contains(lower, "display") { + name := extractName(line) + if name != "" && !isExcluded(name) { + return name + } + } + } + + return "None" + +} + +func getFromSysfsDRM() string { + matches, _ := filepath.Glob("/sys/class/drm/card*") + + for _, path := range matches { + // 驱动名称 + driverLink, err := os.Readlink(filepath.Join(path, "device", "driver")) + if err != nil { + continue + } + driverName := filepath.Base(driverLink) + + // 设备树 compatible 提取具体型号 + // /sys/class/drm/card0/device/of_node/compatible + // "qcom,adreno-750.1\0qcom,adreno" + exactModel := "" + compatibleBytes, err := os.ReadFile(filepath.Join(path, "device", "of_node", "compatible")) + if err == nil { + exactModel = parseSocModel(driverName, compatibleBytes) + } + + // 有具体型号则直接返回 + if exactModel != "" { + return exactModel + } + + // 通用的驱动名称映射 + switch driverName { + case "vc4": + return "Broadcom VideoCore IV/VI (Raspberry Pi)" + case "v3d": + return "Broadcom V3D (Raspberry Pi 4/5)" + case "msm", "msm_drm": + return "Qualcomm Adreno (Unknown Model)" + case "panfrost": + return "ARM Mali (Panfrost)" + case "lima": + return "ARM Mali (Lima)" + case "sun4i-drm", "sunxi-drm": + return "Allwinner Display Engine" + case "tegra": + return "NVIDIA Tegra" + } + + if driverName != "" { + return "SoC GPU (" + driverName + ")" + } + } + + // 开发板 Model + modelData, err := os.ReadFile("/sys/firmware/devicetree/base/model") + if err == nil { + model := string(modelData) + if strings.Contains(model, "Raspberry Pi") { + return "Broadcom VideoCore (Integrated)" + } + } + return "None" } + +// parseSocModel 解析设备树 compatible 字符串,提取人性化名称 +func parseSocModel(driver string, rawBytes []byte) string { + // compatible 文件包含多个以 \0 分隔的字符串 + content := string(bytes.ReplaceAll(rawBytes, []byte{0}, []byte(" "))) + lower := strings.ToLower(content) + + // 高通 Adreno (Qualcomm) + if driver == "msm" || strings.Contains(lower, "adreno") { + // "adreno-750", "adreno-660" + re := regexp.MustCompile(`adreno[-_](\d+)`) + matches := re.FindStringSubmatch(lower) + if len(matches) > 1 { + return "Qualcomm Adreno " + matches[1] + } + return "Qualcomm Adreno" + } + + // ARM Mali (Rockchip/MediaTek/AmLogic) + if driver == "panfrost" || driver == "lima" || strings.Contains(lower, "mali") { + // "mali-g610", "mali-t860" + re := regexp.MustCompile(`mali[-_]([a-z]\d+)`) + matches := re.FindStringSubmatch(lower) + if len(matches) > 1 { + return "ARM Mali " + strings.ToUpper(matches[1]) // Mali G610 + } + return "ARM Mali" // 泛指 + } + + // 树莓派 VideoCore + if driver == "vc4" || driver == "v3d" { + if strings.Contains(lower, "bcm2712") { + return "Broadcom VideoCore VII (Pi 5)" + } + if strings.Contains(lower, "bcm2711") { + return "Broadcom VideoCore VI (Pi 4)" + } + if strings.Contains(lower, "bcm2837") || strings.Contains(lower, "bcm2835") { + return "Broadcom VideoCore IV" + } + } + + // Allwinner (全志) + // "allwinner,sun50i-h6-display-engine" + if strings.Contains(lower, "allwinner") || strings.Contains(lower, "sun50i") || strings.Contains(lower, "sun8i") { + re := regexp.MustCompile(`sun\d+i-([a-z0-9]+)`) + matches := re.FindStringSubmatch(lower) + if len(matches) > 1 { + model := strings.ToUpper(matches[1]) + return "Allwinner " + model + } + return "Allwinner Display Engine" + } + + // NVIDIA Tegra + if driver == "tegra" { + if strings.Contains(lower, "tegra194") { + return "NVIDIA Tegra Xavier" + } + if strings.Contains(lower, "tegra234") { + return "NVIDIA Orin" + } + if strings.Contains(lower, "tegra210") { + return "NVIDIA Tegra X1" + } + } + + return "" +} From c59781dfb3a7582361fd7f919b222b549dc24363 Mon Sep 17 00:00:00 2001 From: Akizon77 Date: Sun, 30 Nov 2025 22:23:52 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E6=8E=92=E9=99=A4=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=20drm=20=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- monitoring/unit/gpu_linux.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/monitoring/unit/gpu_linux.go b/monitoring/unit/gpu_linux.go index 6587868..750f285 100644 --- a/monitoring/unit/gpu_linux.go +++ b/monitoring/unit/gpu_linux.go @@ -103,6 +103,19 @@ func getFromLspci() string { func getFromSysfsDRM() string { matches, _ := filepath.Glob("/sys/class/drm/card*") + excludedDrivers := map[string]bool{ + "virtio-pci": true, + "virtio_gpu": true, + "bochs-drm": true, + "qxl": true, + "vmwgfx": true, + "cirrus": true, + "vboxvideo": true, + "hyperv_fb": true, + "simpledrm": true, + "simplefb": true, + } + for _, path := range matches { // 驱动名称 driverLink, err := os.Readlink(filepath.Join(path, "device", "driver")) @@ -111,6 +124,10 @@ func getFromSysfsDRM() string { } driverName := filepath.Base(driverLink) + if excludedDrivers[driverName] { + continue + } + // 设备树 compatible 提取具体型号 // /sys/class/drm/card0/device/of_node/compatible // "qcom,adreno-750.1\0qcom,adreno"