直接读 /proc/net/dev 是最简单可靠的网络流量统计方案,它无需额外权限、不依赖 libpcap 或 netlink,数据实时准确且与 ip -s link 一致。

直接读 /proc/net/dev 是最简单可靠的方案
Linux 内核通过 /proc/net/dev 暴露每个网络接口的收发字节和包计数,无需额外权限、不依赖 libpcap 或 netlink,且数据与 ifconfig / ip -s link 一致。这是生产环境首选方式。
关键点:
-
/proc/net/dev每行格式固定:前两列是接收(rx)和发送(tx)的包数(packets)、错误数(errs)、丢弃数(drop)等,第2/10列分别是总包数(rx_packets和tx_packets) - 注意跳过前两行表头(
Inter-| Receive和空行),接口名在每行开头,可能带冒号(如eth0:),需 trim 掉 - 文件是实时内核快照,多次读取即可算差值——但别用太短间隔(
std::ifstream 解析 /proc/net/dev 的避坑写法
用 C++ 读这个文件看似简单,但容易因格式空格、对齐、字段偏移错位导致解析失败。别用 operator>> 直接读字符串+整数混合行。
推荐做法:
- 逐行
std::getline(),用std::istringstream+std::skipws跳过任意空白,再按顺序提取 17 个字段(/proc/net/dev固定 17 列) - 只关心第 2 列(
rx_packets)和第 10 列(tx_packets),其余可忽略 - 接口名从行首提取直到遇到第一个非字母数字字符(含冒号),然后
substr(0, pos)截取 - 务必检查
ss.fail()或提取后ss.eof(),防止某行字段不足导致后续全错
示例关键片段:
std::string line;
while (std::getline(file, line)) {
if (line.find(':') == std::string::npos) continue; // 跳过表头
std::istringstream ss(line);
std::string iface;
ss >> iface;
iface = iface.substr(0, iface.find(':')); // 去掉末尾 ':'
unsigned long rx_packets, tx_packets;
// skip 1st, read 2nd (rx_packets), skip 3..9, read 10th (tx_packets)
for (int i = 0; i > std::skipws >> rx_packets;
for (int i = 0; i > std::skipws >> dummy;
ss >> std::skipws >> tx_packets;
if (ss.fail()) continue;
// now use iface, rx_packets, tx_packets
}
为什么不用 netlink 或 getifaddrs()
getifaddrs() 只返回接口状态(UP/DOWN)、地址、MTU 等元信息,**完全不提供任何流量计数字段**——它压根不读 /proc/net/dev,别被名字误导。
netlink(NETLINK_ROUTE + RTM_GETLINK)能拿到部分统计(如 IFLA_STATS64),但:
- 需要自己构造消息、处理多播、解析嵌套属性,代码量大、易出错
- 不是所有内核版本都支持
IFLA_STATS64(尤其老发行版),fallback 到IFLA_STATS又只有 32 位计数,容易溢出 - 相比直接读 proc 文件,性能无优势,还多一次系统调用和内存拷贝
除非你在写内核模块或必须监听接口 up/down 事件,否则纯流量统计场景下,netlink 是过度设计。
内核监控接口?其实你只需要 inotify 监控 /proc/net/dev 变更
有人问“内核监控接口”,以为要注册回调或订阅事件。实际上 /proc 是虚拟文件系统,内核不会主动通知用户空间文件内容变更——但你可以用 inotify 监听其 mtime 或 size 变化,作为轻量触发信号。
不过要注意:
-
/proc/net/dev的 mtime 不会随每次更新而改变(内核不更新时间戳),所以不能监听IN_MODIFY;只能轮询或监听IN_ACCESS(不推荐) - 真正实用的是:用
inotify_add_watch(fd, "/proc/net", IN_MOVED_TO | IN_CREATE)——因为内核有时会重建整个/proc/net/目录(如加载新协议模块),此时/proc/net/dev文件 inode 可能变化,IN_MOVED_TO可捕获重载信号 - 但绝大多数场景下,简单定时读取比 inotify 更稳——毕竟
/proc读取开销极低,且避免了 inotify fd 泄漏、事件队列满等边界问题
真要低延迟响应,不如把采样周期设为 200ms 并用 clock_gettime(CLOCK_MONOTONIC) 对齐,比折腾 inotify 实惠得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











