最直接获取网卡物理链路速率的方法是:linux 用 ethtool 查询硬件协商结果(如 speed: 1000mb/s),windows 用 getifentry2 获取 transmitlinkspeed/receivelinkspeed;/sys/class/net/*/speed 不可靠,应避免使用。

Linux 下用 ethtool 查网卡速率最直接
系统级速率(比如物理链路协商的 1Gbps、2.5Gbps)不是靠读取流量统计得来的,而是查硬件协商结果。ethtool 就是干这个的——它直接和内核驱动通信,拿到真实 link settings。
常见错误是试图从 /proc/net/dev 或 ifconfig 输出里“算”速率:那些只反映当前收发字节数,完全不体现物理能力。
-
ethtool eth0输出里的Speed:行就是你要的值(单位 Mbps),例如Speed: 1000Mb/s - 脚本中提取可用:
ethtool eth0 | awk '/Speed:/ {print $2}' | tr -d 'Mb/s' - 注意权限:非 root 可能看不到完整信息,尤其某些厂商驱动(如 mellanox)会限制
- 虚拟网卡(
veth、docker0)通常返回Unknown!,因为没物理 link —— 这不是 bug,是设计如此
Windows 下用 GetIfEntry2 替代已废弃的 GetIfEntry
GetIfEntry 在 Windows Vista 后就基本失效了,它返回的 dwSpeed 字段对千兆以上网卡常为 0 或错误值。必须用 GetIfEntry2,它通过 IF_ENTRY_TABLE2 返回真实 TransmitLinkSpeed 和 ReceiveLinkSpeed(单位是 bps)。
- 调用前需
InitializeIpInterfaceEntry+GetIfEntry2,不能只靠接口索引硬查 - 关键字段是
InterfaceLuid,不是IfIndex—— 后者在热插拔或重命名后可能复用,导致查错设备 - 返回值
TransmitLinkSpeed是 uint64_t,别用DWORD接收,否则高位截断(万兆卡会显示成 0) - 如果返回
IF_OPER_STATUS_NON_OPERATIONAL,TransmitLinkSpeed无效,此时应视为“未连接”,不要强行用 0 填充
C++ 跨平台封装要注意的三个硬伤
想写个统一接口?别急着抽象。Linux 和 Windows 的“速率”语义根本不同:Linux 的 ethtool 返回的是 PHY 协商结果;Windows 的 GetIfEntry2 返回的是驱动上报的链路能力,但某些 Realtek 驱动会把节能模式下的降速也写进去。
立即学习“C++免费学习笔记(深入)”;
- 别用同一个字段名存两者:C++ 类里分开叫
phy_speed_mbps(Linux)和link_speed_bps(Windows),避免误用单位 - Windows 下需链接
iphlpapi.lib,且最低支持 Windows 7 SP1 —— XP/Vista 用户会失败,得 fallback 到 WMI 查询(但延迟高、不可靠) - Linux 上别
fork+exec("ethtool")做跨进程调用:容器环境可能没ethtool二进制,或被 seccomp 拦截;改用 netlink(NETLINK_ROUTE)更底层但稳定
为什么不用 /sys/class/net/eth0/speed?
这个 sysfs 文件看着方便,但它有严重缺陷:只在 link up 时存在,且内容是字符串 “1000”,没单位、不校验有效性。更麻烦的是,某些内核版本(如 5.15+ 的某些 ARM 平台)默认禁用该接口,读出来是空或 -ENODEV。
- 它本质是
ethtool的简化代理,稳定性不如直接调用ioctl(SIOCETHTOOL) - 如果程序需要持续轮询,反复 open/read 该文件比单次
ethtool调用开销更大(涉及多次 VFS 路径解析) - 遇到 bond/team 聚合口,这里永远返回 0 —— 它压根不处理逻辑聚合层,只认物理子接口
真要轻量,宁可自己封装一个最小 ethtool ioctl 调用,别碰这个路径。










