/proc/diskstats第6列(读扇区数)和第10列(写扇区数)为自启动累计值,单位512字节,需两次采样差分并除以时间间隔得每秒字节数;应选无数字后缀主设备(如sda),优先用lsblk确认设备名。

Linux下用/proc/diskstats解析磁盘I/O累计字节数
Linux内核通过 /proc/diskstats 暴露底层块设备统计,其中第6列(写入扇区数)和第3列(读取扇区数)是关键——但注意:它们是**自启动以来的累计值,单位是扇区(通常512字节)**,不是每秒速率。你要的“每秒累计读取字节数”实际是速率,需两次采样做差分。
常见错误是直接读一次就当实时速率,或误把第2列(设备名)当主磁盘——sda 不一定对应你的根分区,nvme0n1 也可能才是主盘。建议先用 lsblk 确认目标设备名。
- 读取
/proc/diskstats后按空格分割,跳过前2列(主次设备号),第3列为读取扇区数,乘以512得字节数 - 两次读取间隔建议 ≥1秒,否则差值可能为0;间隔太长(如10秒)会平滑掉短时峰值
- 注意:同一物理磁盘可能有多个条目(如
sda和sda1),应优先选无数字后缀的主设备(sda而非sda1)
Windows下用PdhAddCounter获取PhysicalDiskDisk Read Bytes/sec
Windows没有直接暴露累计字节数的文件接口,必须用性能计数器(PDH)。PhysicalDiskDisk Read Bytes/sec 是瞬时速率(单位字节/秒),它本身已是“每秒读取字节数”,但要注意:它是采样窗口内的平均值,不是严格意义上的导数。
容易踩的坑是没调用 PdhCollectQueryData 就直接读值,此时返回 PDH_INVALID_DATA;或者忘记初始化 PdhOpenQuery 和 PdhAddCounter 的调用顺序。
- 计数器路径格式为
\<em>computer</em>PhysicalDisk(<em>instance</em>)\Disk Read Bytes/sec,instance 可用_Total或具体盘符(如0 C:) - 首次调用
PdhCollectQueryData后需等待至少1秒再第二次调用,否则值无效 - 返回值类型是
PDH_FMT_COUNTERVALUE,需检查dwType == PDH_FMT_DOUBLE再取doubleValue
C++跨平台封装建议:避免硬编码路径与计数器名
硬写 /dev/sda 或 _Total 会导致程序在不同机器上失效。Linux下可用 stat("/sys/block/*/device", &st) 遍历块设备,再结合 /proc/mounts 找到挂载点为 / 的设备;Windows下可用 GetLogicalDrives() 获取有效盘符,再构造对应计数器路径。
更关键的是时间同步问题:两次采样若用 std::chrono::steady_clock 测间隔,但系统时间被NTP校准可能导致负差值——应只依赖单调时钟测间隔,不用于绝对时间对齐。
- Linux采样函数返回
std::pair<uint64_t std::chrono::time_point></uint64_t>,包含扇区数和采样时刻,差分时只用时间差作分母 - Windows不要用
PDH_FMT_LONG,某些系统返回PDH_FMT_LARGE,强制转long long更安全 - 异常处理必须覆盖:文件不存在(Linux)、计数器未启用(Windows)、权限不足(需root/Administrator)
为什么不能直接用statvfs或GetDiskFreeSpaceEx
statvfs 和 GetDiskFreeSpaceEx 只提供剩余/总空间,不记录I/O历史。有人试图用反复调用 read() 并累加字节数来模拟,但这仅统计本进程读取量,漏掉内核缓存、mmap、其他进程等所有真实磁盘活动——完全偏离“系统级磁盘读取统计”的需求。
真正要监控的是块设备层(block layer)的原始读取,不是文件系统层(VFS)的逻辑读。这也是为什么必须走 /proc/diskstats 或 PDH,而不是任何用户态文件操作API。
差分计算时,扇区计数器可能回绕(虽然极罕见),但比直接信任单次读值靠谱得多;Windows计数器本身已处理回绕,不用额外判断。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











