/proc/diskstats 中第6、10列是累计扇区数(512字节),需两次采样求差得速率;不可误用i/o次数列或直接当瞬时值,且须区分整盘与分区、注意字段索引稳定性及跨平台pdh原始值转换。

Linux 下用 /proc/diskstats 解析磁盘 I/O 累计字节数
Linux 内核通过 /proc/diskstats 暴露块设备的底层 I/O 统计,其中第 6 列(read_sectors)和第 10 列(write_sectors)是扇区数(512 字节),可换算为字节数。注意:这不是“每秒”值,而是自系统启动以来的累计值,需两次采样做差才能得速率。
常见错误是直接读取后就当瞬时速率用;或者误把第 3/7 列(read_ios/write_ios)当字节数——它们是 I/O 请求次数,不是字节量。
- 先用
cat /proc/diskstats | grep -E "^(sd|nvme|vd)"确认目标设备名(如sda、nvme0n1) - 按空格分割行,跳过前两列(主次设备号),取第 6 和第 10 字段(C++ 中
std::istringstream+std::vector<:string></:string>更稳,别用strtok) - 扇区转字节:乘以 512;若设备使用逻辑扇区(如某些 NVMe),实际物理扇区可能不同,但
/proc/diskstats始终按 512 字节扇区上报 - 同一设备可能有多个条目(如
sda和sda1),要区分是整个盘还是分区——看第 3 列是否为 0(主设备号),或查/sys/block/<dev>/device/name</dev>
Windows 下调用 GetDiskFreeSpaceEx 不行,得用 PDH 或 WMI
GetDiskFreeSpaceEx 只返回剩余/总空间,不提供 I/O 计数器。PDH(Performance Data Helper)是 Windows 官方推荐方式,能读取 PhysicalDiskDisk Read Bytes/sec 这类性能计数器,但它默认返回的是“每秒平均值”,不是累计字节数——你得自己累加。
关键点:PDH 计数器类型是 PERF_COUNTER_BULK_COUNT,原始值(RawValue)才是累计扇区数(需 ×512),而 CounterValue 是经 PDH 库除以时间间隔算出的速率。很多代码只取 CounterValue,就永远拿不到累计值。
- 初始化用
PdhOpenQuery,添加计数器用PdhAddCounter,路径形如\PhysicalDisk(_Total)\Disk Read Bytes/sec - 调用
PdhCollectQueryData后,用PdhGetFormattedCounterValue获取PDH_FMT_LARGE格式原始值(long long),这才是累计字节数 - 如果需要精确到某块盘(非 _Total),计数器路径中设备名必须与
diskperf -y启用后在性能监视器里看到的一致(如0 C:或0 D:),不能写成C:
C++ 跨平台封装要注意的三件事
没有标准库函数能直接跨平台拿到磁盘累计字节数,硬封装时最容易在三个地方翻车:
- Linux 下
/proc/diskstats行格式不稳定:新版内核新增字段(如第 14+ 列的 discard 统计),但前 11 列顺序不变;务必按列索引取值,别依赖std::string::find查关键词 - Windows 的 PDH 查询必须在主线程或明确设置 COM 套间(
CoInitializeEx(NULL, COINIT_MULTITHREADED)),否则PdhCollectQueryData返回PDH_INVALID_HANDLE - 累计值是无符号 64 位整数,但 Windows PDH 的
LargeValue字段是FILETIME类型(两个DWORD),需用ULARGE_INTEGER联合体转换,不能直接 reinterpret_cast
“每秒”统计必须自己做两次采样差值
所有平台提供的都是累计值,所谓“每秒”只是应用层概念。没有现成的“当前每秒速率”API——你必须至少间隔 1 秒采样两次,再除以时间差(单位秒)。误差主要来自采样间隔抖动和计数器更新时机。
- 别用
sleep(1),精度差且阻塞;改用std::this_thread::sleep_until配合高精度时钟(std::chrono::steady_clock)控制采样周期 - Linux 下两次读
/proc/diskstats时间差可能不足 1 秒,导致除零或负值;Windows PDH 若两次PdhCollectQueryData间隔太短,CounterValue可能未更新,返回 0 - 建议最小采样间隔设为 500ms,计算时用实际经过的纳秒差(
duration_cast<:chrono::nanoseconds>(end_time - start_time).count()</:chrono::nanoseconds>),再换算成秒(double)
真正麻烦的不是读数据,而是对齐采样时刻、处理计数器回绕(极少见但理论上存在)、以及区分物理盘与逻辑卷——这些细节不写进日志,排查时会卡半天。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











