You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不依赖CLI工具通用获取网卡bus-info?含Go实现需求

通用获取网卡bus-info的方法(Go实现优先)

核心思路

兼容物理机(PM)和虚拟机(VM)环境,优先通过sysfs读取(无外部依赖),失败后降级调用系统工具:

  1. 先尝试读取网卡设备的uevent文件,提取PCI_SLOT_NAME字段(物理机直接可用);
  2. 若uevent无对应字段,解析网卡设备目录的真实路径,提取其中符合PCI总线格式的段;
  3. 若上述方法都失败,调用ethtool命令解析输出(需系统预装ethtool)。

Go语言实现代码

package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

// GetBusInfo 获取指定网卡的bus-info
func GetBusInfo(iface string) (string, error) {
	// 步骤1:读取uevent文件的PCI_SLOT_NAME
	ueventPath := fmt.Sprintf("/sys/class/net/%s/device/uevent", iface)
	file, err := os.Open(ueventPath)
	if err == nil {
		defer file.Close()
		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			line := scanner.Text()
			if strings.HasPrefix(line, "PCI_SLOT_NAME=") {
				return strings.TrimPrefix(line, "PCI_SLOT_NAME="), nil
			}
		}
		if err := scanner.Err(); err != nil {
			return "", err
		}
	}

	// 步骤2:解析device目录的真实路径,提取PCI总线信息
	devicePath := fmt.Sprintf("/sys/class/net/%s/device", iface)
	realDevicePath, err := filepath.EvalSymlinks(devicePath)
	if err != nil {
		return "", err
	}
	// 匹配PCI总线格式:xxxx:xx:xx.x
	pciRegex := regexp.MustCompile(`[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]`)
	matches := pciRegex.FindStringSubmatch(realDevicePath)
	if len(matches) > 0 {
		return matches[0], nil
	}

	// 步骤3:降级调用ethtool命令
	cmd := exec.Command("ethtool", "-i", iface)
	output, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("ethtool failed: %w", err)
	}
	scanner := bufio.NewScanner(strings.NewReader(string(output)))
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "bus-info:") {
			return strings.TrimSpace(strings.TrimPrefix(line, "bus-info:")), nil
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	return "", fmt.Errorf("failed to get bus-info for interface %s", iface)
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s <interface>\n", os.Args[0])
		os.Exit(1)
	}
	busInfo, err := GetBusInfo(os.Args[1])
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("Bus-info for %s: %s\n", os.Args[1], busInfo)
}

代码说明

  • sysfs读取:直接访问Linux内核暴露的sysfs文件系统,无外部依赖,效率最高;
  • 路径解析:利用filepath.EvalSymlinks获取真实路径,通过正则匹配PCI总线格式,兼容VM环境下的多层路径;
  • ethtool降级:作为兜底方案,覆盖极少数特殊环境。

sysfs路径差异的原因

物理机和虚拟机的网卡sysfs路径差异,本质是设备虚拟化层的存在:

  • 物理机:网卡是直接挂载在PCI总线下的物理设备,sysfs路径为/sys/devices/pci<domain>:<bus>/<slot>:<func>.<dev>/net/<iface>,PCI总线信息直接位于net目录的父节点;
  • 虚拟机:网卡是虚拟化设备(如virtio、vmxnet3),这类设备会先映射为虚拟机内部的虚拟PCI设备,再通过虚拟化驱动(如virtio-net)暴露为网卡接口,因此sysfs路径多了一层虚拟化设备节点,比如/sys/devices/pci<domain>:<bus>/<slot>:<func>.<dev>/virtio<X>/net/<iface>,PCI总线信息位于虚拟化节点的父节点。

这种差异是Linux内核对不同类型设备的sysfs组织规则导致的——物理设备直接关联总线,虚拟化设备则需要通过中间层关联到宿主机的物理设备。

内容的提问来源于stack exchange,提问作者DesistDaydream

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 16:27:27