Linux下计算I/O繁忙度需两次采样/proc/diskstats的io_ticks(第10列)求差再除以时间间隔,优先取主设备行(如sda、nvme0n1),间隔≥100ms;Windows则用PDH API获取% Disk Time计数器,二者本质均为(忙时毫秒/采样间隔毫秒)×100。
/proc/diskstats计算I/O繁忙度Linux没有直接的“磁盘I/O负载百分比”系统调用,最可靠的方式是周期性采样/proc/diskstats,通过io_ticks(第10列)字段推算设备忙时占比。该值单位为毫秒,表示设备驱动层处理I/O所花的总时间(含队列等待),需两次采样做差再除以采样间隔。
注意:同一块物理磁盘可能对应多个设备名(如sda、sda1、sda2),应优先统计主设备(无数字后缀);NVMe盘则为nvme0n1这类命名,其io_ticks同样有效。
实操建议:
fopen("/proc/diskstats", "r")打开并逐行解析,跳过开头非设备行(如空行或注释)io_ticks未更新,结果为0io_ticks可能停滞,需检查两次值是否相等并跳过该周期PdhAddCounter获取% Disk Time
Windows不提供类似/proc的轻量接口,必须走PDH(Performance Data Helper)API。核心是添加计数器PhysicalDisk(_Total)% Disk Time或指定盘符(如PhysicalDisk(0 C:)% Disk Time),它本质是(disk busy ms / sample interval ms)×100,与Linux逻辑一致但封装更厚。
立即学习“C++免费学习笔记(深入)”;
常见错误现象:
PdhCollectQueryData返回PDH_INVALID_HANDLE:未先调用PdhOpenQuery和PdhAddCounter
PDH_INVALID_DATA:权限不足(需管理员运行)或目标盘符不存在(如查C:但系统盘是D:)PdhCollectQueryData触发实际采集,或间隔太短(PDH内部有最小刷新周期)关键步骤顺序不可乱:PdhOpenQuery → PdhAddCounter → PdhCollectQueryData → 等待≥1s → 再次PdhCollectQueryData → PdhGetFormattedCounterValue。
别试图用std::filesystem::space或statvfs替代——它们只返回磁盘剩余空间,和I/O负载完全无关。也别依赖iotop或iostat命令行输出,解析不稳定且启动开销大。
性能与兼容性要点:
/proc/diskstats是伪文件,每次读取开销极低,可每200ms采样一次;Windows PDH每秒最多安全调用1–2次,高频会失败IOBSDNameMatching + IORegistryEntryCreateCFProperties只能查设备状态,无法获取实时忙时,实际项目中常降级为“最近10秒是否有I/O”布尔判断/proc/diskstats默认可见,但若挂载了procfs子集(如ro,hidepid=2),可能读不到第10列,需确认容器权限libstatgrab或sysinfo类第三方库这些库底层仍是封装上述系统接口,但抽象层会隐藏关键细节:比如libstatgrab的sg_get_disk_io_stats不暴露io_ticks原始值,只返回已换算的“busy percent”,而该换算假设采样间隔恒定,一旦程序卡顿就会失真;sysinfo对Windows PDH的封装甚至硬编码了1秒间隔,无法适配需要更高精度的场景。
真实项目里,直接对接原生接口才能控制采样节奏、处理异常值、区分物理盘与逻辑卷。复杂点在于Linux要自己解析/proc文本格式,Windows要写一整套PDH错误检查循环——但这两处恰恰是I/O负载不准的根源,绕不开。