Linux下计算出口带宽利用率需两次读取/proc/net/dev的tx_bytes(第10列)求差,除以时间间隔和网卡标称带宽(如千兆网填125000000字节/秒),须排除lo等虚拟接口、动态识别UP状态物理网卡,并确保采样间隔≥100ms。

Linux 下用 /proc/net/dev 计算出口带宽利用率
操作系统本身不直接提供“出口带宽利用率”这个值,得自己算:用两次读取 /proc/net/dev 的字节数差值,除以时间间隔,再除以网卡标称带宽(比如 1Gbps = 125 MB/s)。关键不是“实时”,而是采样频率和接口选择。
常见错误是把 rx_bytes(入口)当 tx_bytes(出口);或者没排除 lo、docker0 等虚拟接口,导致数据失真。实际出口一般对应物理网卡名,如 eth0 或 ens33,可通过 ip -br link | grep UP 粗筛。
- 每次读取后需缓存上一次的
tx_bytes值,不能每次都从零开始 - 时间间隔建议 ≥ 100ms,太短会导致浮点噪声大;
std::this_thread::sleep_for(100ms)是合理下限 - 标称带宽需手动配置(无法自动识别),千兆网卡填
125000000字节/秒,万兆则为1250000000 - 注意
/proc/net/dev每行格式固定,第 10 列是tx_bytes,用空格/制表符分割时要跳过开头的接口名(含冒号)
延迟统计得靠 ping 或 ICMP 自实现,getsockopt 不管用
别指望 getsockopt(fd, SOL_SOCKET, SO_RCVBUF, ...) 或类似系统调用返回网络延迟——它只管 socket 缓冲区,和 RTT 完全无关。真实延迟必须发包测,最轻量的是构造 ICMP Echo 请求(即用户态 ping 行为),或复用已建立的 TCP 连接发小包+记录 send() 到 recv() 的耗时。
用 system("ping -c 1 -W 1 8.8.8.8") 看似简单,但启动新进程开销大、难控制超时、输出解析脆弱。更稳的方式是用原始套接字(AF_INET + SOCK_RAW + IPPROTO_ICMP),自己组包、发包、收包、校验、计时。
立即学习“C++免费学习笔记(深入)”;
- 非 root 用户无法创建
SOCK_RAW,需加sudo setcap cap_net_raw+ep ./your_app或改用普通 TCP 测(如连8.8.8.8:53后立即close,但受服务端响应策略影响) - 单次 ping 延迟波动大,建议连续发 5–10 包,丢包率 > 20% 就该标为异常,而非只取平均值
-
clock_gettime(CLOCK_MONOTONIC, &ts)比std::chrono::steady_clock更准,尤其在系统时间被 NTP 调整时
Windows 上绕不开 GetIfEntry2 和 ICMPv4Echo
Windows 没有 /proc/net/dev,得调 iphlpapi.dll。出口流量要用 GetIfEntry2 查指定 IfIndex 的 OutOctets 字段,配合轮询计算速率;延迟则只能走 IcmpCreateFile + IcmpSendEcho2(新版推荐用 ICMPv4Echo 结构体),不能用 WSASendTo 自组 ICMP——Win10+ 默认禁用 raw socket 发 ICMP。
容易踩的坑是 IfIndex 不稳定:重启、热插拔、驱动更新都可能让索引变,必须每次用 GetIfTable2 扫描,匹配 InterfaceDescription 或 Luid(后者更可靠);另外 OutOctets 是累计值,溢出后会回绕,需用无符号减法处理(a - b 在 uint64_t 下天然支持回绕)。
- 调
GetIfEntry2前必须先InitializeIpForwardEntry?不用,那是路由表操作,纯查接口状态只需链接iphlpapi.lib -
IcmpSendEcho2返回的RTT是毫秒整数,精度不如用户态计时,但胜在免权限;若要亚毫秒级,得用QueryPerformanceCounter手动打点 - Windows 的“出口”定义更模糊:有多个活跃接口时(WiFi + 以太网),系统按跃点数选默认路由,
GetBestRoute2可查当前出口设备
跨平台封装要注意时序对齐和指标语义一致性
一个进程里同时跑 Linux 的 /proc 读取和 Windows 的 GetIfEntry2 调用?不行。必须预编译分支,或运行时检测 OS 后加载对应模块。更大的问题是“出口”的语义漂移:Linux 默认路由接口可能和 Windows 的 GetBestRoute2 结果不同;而延迟目标地址若硬编码为 8.8.8.8,在某些内网环境根本不通,应允许配置 DNS 或 fallback 到本地网关 IP。
性能上,每 200ms 读一次 /proc/net/dev 几乎无开销,但每秒发 5 个 ICMP 包在低配设备上可能占满单核;延迟统计建议用独立线程+环形缓冲区(std::array + 原子索引),避免阻塞主逻辑。
- 不要把带宽和延迟塞进同一个结构体返回,它们采样周期、误差来源、更新时机完全不同
- 单位务必显式标注:
bytes_per_second、milliseconds,别依赖注释——序列化到 JSON 或日志时容易错乱 - 首次采集无历史值时,延迟可设为 -1,带宽设为 0,而不是用 NaN 或 throw exception,下游更好处理


















