Linux下读取/proc/diskstats是C++程序稳定获取实时IO吞吐(B/s)的最直接方案,通过两次采样第10/11列(read_bytes/write_bytes)求差并除以时间差即可;Windows无等价轻量接口,PDH等API依赖权限、延迟高且不稳定。

Linux下读取/proc/diskstats是最直接可靠的方式
Windows没有标准跨版本API能实时获取原始IO吞吐量(B/s),而Linux内核通过/proc/diskstats持续暴露每块设备的累计读写字节数和I/O计数,只要两次采样间隔足够短(如1秒),就能算出瞬时吞吐。这是C++程序实际能稳定落地的方案。
注意/proc/diskstats每一行对应一个设备(含分区),关键字段是第3列(读取扇区数)、第7列(写入扇区数)和第10列(读取字节数)、第11列(写入字节数)——但推荐用第10/11列,因为它们是真实字节数,不依赖512字节扇区假设(NVMe等设备可能不同)。
- 需按设备名过滤(如
sda、nvme0n1),避免把分区(sda1)和主设备重复计算 - 必须做两次读取+差值计算,单次读取只有累计值,无意义
- 建议用
std::ifstream逐行解析,跳过空行和注释行;字段用std::istringstream提取,避免scanf格式错误崩溃 - 注意:该文件无锁,多进程同时读安全,但同一进程内两次读取之间不要sleep过久,否则数值溢出或被其他IO干扰
如何用C++解析并计算每秒吞吐(B/s)
核心是记录上一次的read_bytes和write_bytes,与当前值相减后除以时间差。不能只看单方向,应分别计算读吞吐、写吞吐、总吞吐。
示例关键逻辑:
struct DiskIoSample {
uint64_t read_bytes = 0;
uint64_t write_bytes = 0;
std::chrono::steady_clock::time_point timestamp;
};
DiskIoSample read_disk_stats(const std::string& device_name) {
std::ifstream f("/proc/diskstats");
DiskIoSample s;
std::string line;
while (std::getline(f, line)) {
std::istringstream iss(line);
std::string major, minor, name;
iss >> major >> minor >> name;
if (name == device_name) {
// 跳过前9列,读第10列(read_bytes)和第11列(write_bytes)
for (int i = 0; i > std::ws;
iss >> s.read_bytes >> s.write_bytes;
break;
}
}
s.timestamp = std::chrono::steady_clock::now();
return s;
}
- 字段索引从0开始,第10列是索引9(read_bytes),第11列是索引10(write_bytes)
- 务必检查
iss是否成功提取,否则s.read_bytes保持0导致结果为负 - 时间差用
std::chrono::duration_cast<:chrono::milliseconds></:chrono::milliseconds>转毫秒再除1000.0,避免整数除法截断 - 设备名必须精确匹配(
"sda"≠"sda1"),可先读/proc/partitions确认有效设备列表
Windows上只能间接估算,且结果不稳定
Windows没有等价于/proc/diskstats的轻量级接口。GetDiskFreeSpaceEx只给剩余空间;Performance Counter(如PhysicalDiskDisk Bytes/sec)依赖WMI或PDH,启动慢、权限要求高、采样延迟大(默认最小1秒,且首次查询常返回0)。
- 使用
PdhAddCounter注册计数器后,必须调用PdhCollectQueryData至少两次(第一次预热),第二次才有效 - 计数器路径形如
\<machine>\PhysicalDisk(_Total)\Disk Bytes/sec</machine>,其中_Total是汇总,单个磁盘需替换为0 C:等实例名,但实例名需动态枚举,不可硬编码 - PDH API返回的是
double类型速率值,单位是B/s,但底层仍是采样差值,若系统IO极低,可能长时间返回0或NaN - 多数生产环境C++程序会直接放弃Windows IO吞吐监控,改用日志聚合或代理采集(如Telegraf),而非嵌入式实时计算
容易被忽略的边界问题
吞吐量不是“越大越好”的指标,计算本身简单,但解读极易出错。
-
/proc/diskstats中read_bytes包含缓存命中读(page cache),不代表真实磁盘IO;真正想看物理IO,得结合/proc/diskstats第2列(reads completed)和第6列(writes completed)算IOPS,再乘平均IO大小 - 虚拟机或容器环境下,宿主机的
sda对客户机可能是vdX或xvda,设备名需适配,不能写死 - SSD/NVMe设备的队列深度和延迟特性,会让短时吞吐剧烈抖动(如100ms内突增200MB),单纯看1秒平均值可能掩盖毛刺,必要时需保留原始采样点做滑动窗口统计
- C++程序若以root运行,注意
/proc/diskstats权限始终为644,无需特权;但若用WMI或PDH,Windows下必须管理员权限,否则PdhOpenQuery失败返回PDH_ACCESS_DENIED
真正难的不是读数字,而是确定你到底想监控什么:是用户感知的存储延迟?还是后端磁盘的物理饱和度?前者该看iostat -x的%util和await,后者才盯吞吐。选错指标,代码写得再准也没用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











