linux 下通过 /proc/diskstats 第6、10列(扇区数)结合 stat() 获取设备号匹配主次设备号,定时采样计算差值可得磁盘i/o速率;windows 则需用 pdh 查询 logicaldisk 计数器,注意实例名映射与两次采集校准。

Linux 下用 /proc/diskstats 解析磁盘 I/O 累计字节数
直接读 /proc/diskstats 是最轻量、最可靠的方式。它不依赖外部工具,也不需要 root 权限,只要程序能读文件就行。每行代表一个设备(如 sda、nvme0n1),第 6 列是累计读取字节数,第 10 列是累计写入字节数(单位:扇区,512 字节)。
注意:不是所有行都对应物理磁盘——虚拟设备(如 loop0、dm-0)或分区(如 sda1)也会出现在里面。要统计“当前程序所在磁盘”的 I/O,得先确定程序运行在哪个块设备上,通常靠 /proc/mounts 或 stat("/proc/self/exe", &st) 获取所在文件系统的设备号,再映射到 /proc/diskstats 中的主次设备号。
- 解析时跳过空行和注释行(以
#开头) - 字段按空白符分割,但设备名可能含空格(如
md127),实际应按前 3 列固定宽度或用sscanf按格式"%d %d %s %llu %llu %llu %llu %llu %llu %llu ..."提取 -
st_dev从stat()得到的是major:minor编码值,需用major(st.st_dev)和minor(st.st_dev)(需<sys></sys>)与/proc/diskstats前两列比对
C++ 定时采样并计算每秒速率的关键点
“每秒累计”本质是两个时间点的差值除以时间间隔,不能靠单次读取。必须至少两次采样,且间隔尽量稳定(比如 1 秒),否则速率失真。别用 sleep(1)——它不精确,受调度影响大;建议用 std::this_thread::sleep_until 配合高精度时钟校准。
- 用
std::chrono::steady_clock记录每次采样时间戳,避免系统时间跳变干扰 - 读取
/proc/diskstats后只缓存目标设备的read_sectors和write_sectors(即第 6、10 列),不反复解析全文件 - 第一次采样后暂不输出速率,等第二次才有 Δ 值;初始速率为 0 是合理行为
- 若两次采样间隔远大于 1 秒(如卡顿、调试断点),应丢弃该组数据,或按实际 Δt 计算(避免误报“瞬时爆炸”)
Windows 下用 PDH 查询 LogicalDisk 的 Disk Read Bytes/sec 和 Write Bytes/sec
Windows 没有类 Unix 的统一 proc 接口,必须走性能计数器(PDH)。关键在于:计数器路径需指定具体实例(如 \PhysicalDisk(_Total)\Disk Read Bytes/sec),而“当前程序所在磁盘”对应的是其所在卷的盘符(如 C:),需先调用 GetVolumeNameForVolumeMountPoint 或 GetDriveType 确认,再转成 PDH 实例名(C: → C:,但注意 _Total 不等于所有盘之和,它是硬件级聚合)。
- 初始化 PDH 用
PdhOpenQuery,添加计数器用PdhAddCounter,路径格式严格:\<hostname>\LogicalDisk(<instance>)\Disk Read Bytes/sec</instance></hostname> - 首次调用
PdhCollectQueryData只是预热,必须调用两次(间隔 ≥1 秒)才能得到有效速率值 - 结果类型是
PDH_FMT_LARGE,需用PdhGetFormattedCounterValue提取largeValue字段,单位已是字节/秒,无需换算 - 如果程序在 WSL 或容器中运行,PDH 无法获取宿主机磁盘数据——此时应回退到 Linux 方式(WSL2 底层仍是 Linux 内核)
跨平台封装时最容易被忽略的细节
很多人试图写一个“自动识别平台+自动选磁盘”的通用函数,结果在边界场景崩溃。真正稳定的封装必须明确割裂逻辑:平台判定(#ifdef _WIN32)、设备发现(mount point → dev_t → diskstats 匹配 or volume → PDH instance)、采样控制(独立线程+环形缓冲区存最近 N 组数据)三者不可耦合。
-
/proc/diskstats中设备名可能动态变化(热插拔 NVMe、udev 规则重命名),不要硬编码"sda";始终通过stat()的st_dev关联 - Windows 的 PDH 计数器存在延迟:首次收集可能返回
PDH_INVALID_DATA,需循环等待直到PdhCollectQueryData返回ERROR_SUCCESS - 速率值可能出现负数(计数器回绕或内核 bug),应检查 Δsector 是否为负,若是则跳过本次更新
- 不要把“程序自身 I/O”和“整盘 I/O”混淆——
/proc/diskstats统计的是设备级,无法过滤出单个进程;若需进程级,得换用/proc/[pid]/io(但那是字节总量,非速率)
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











