linux下直接读/proc/diskstats最轻量可靠,第3列读扇区数、第7列写扇区数,乘512得字节数;需匹配sda等主设备名,用std::stoull解析unsigned long long防溢出,避免分区重复统计。

Linux下用/proc/diskstats解析磁盘I/O累计字节数
直接读取/proc/diskstats是最轻量、最可靠的方式。它不依赖额外库,输出稳定,且字段含义明确(第3列是读取扇区数,第7列是写入扇区数,每扇区512字节)。
- 注意只解析你关心的设备行,比如
sda或nvme0n1,避免匹配到分区(如sda1)导致重复统计 - 逐行读取时用空格+制表符分割,跳过空行和注释行;推荐用
std::istringstream配合skipws处理空白 - 扇区数是
unsigned long long类型,必须用%llu或std::stoull()解析,否则溢出会导致负值或截断 - 示例片段:
std::string line; while (std::getline(file, line)) { std::istringstream iss(line); std::string dev; unsigned long long rd_sectors, wr_sectors; if (iss >> std::ws >> dev >> std::ws >> rd_sectors >> std::ws >> wr_sectors && dev == "sda") { uint64_t bytes_read = rd_sectors * 512; uint64_t bytes_written = wr_sectors * 512; // 使用bytes_read / bytes_written } }
Windows下用GetDiskFreeSpaceEx和性能计数器的区别
GetDiskFreeSpaceEx只能拿到剩余/总空间,完全不提供I/O字节统计——这是常见误解。真正可用的是Windows性能计数器(PDH API),但必须指定正确的计数器路径和实例名。
- 计数器路径形如
\PhysicalDisk(_Total)\Disk Read Bytes/sec,但_Total汇总值可能不准;更稳的做法是枚举所有物理磁盘实例(用PdhEnumObjectItems),再为每个\PhysicalDisk(0 C:)\...单独查询 - 首次调用
PdhAddCounter后必须先调用一次PdhCollectQueryData,否则PdhGetFormattedCounterValue返回PDH_INVALID_DATA - 计数器返回的是“每秒速率”,不是累计值;若需累计,得自己做积分(记录时间戳+速率,数值相乘累加),且要注意采样间隔抖动
- 权限方面:某些计数器需要
SE_PERFORMANCE_MONITOR_NAME特权,普通用户进程可能失败,建议用PDH_FMT_LARGE和PDH_FMT_NOSOURCE降低失败概率
跨平台封装时避开statvfs和sysctl陷阱
别用statvfs——它只返回文件系统空闲/总量,和I/O字节完全无关。sysctl(macOS/BSD)虽有kern.diskstats,但格式不稳定,字段顺序可能随版本变化,且不包含扇区到字节的换算系数。
- macOS推荐走
IOKit:用IOServiceGetMatchingServices匹配IOBlockStorageDevice类,再调用IORegistryEntryCreateCFProperties获取IOBSDName和IOGeneralInterest中的Statistics字典,其中ReadBytes/WriteBytes字段即为累计字节数 - 跨平台代码里,Linux分支必须检查
/proc/diskstats是否存在且可读(容器环境可能挂载受限);Windows分支要容忍PdhOpenQuery失败并降级为日志告警 - 不要假设设备名一致:Linux的
nvme0n1在macOS可能是disk0,需按逻辑卷或UUID映射,而非硬编码设备名
为什么不能靠read/write系统调用Hook来统计
用户态Hook(如LD_PRELOAD替换write)漏掉太多路径:内存映射写入(mmap+msync)、Direct I/O、内核线程发起的刷盘(如pdflush)、甚至某些libc缓冲策略(如setvbuf全缓冲)都会绕过你的Hook。
- 即使Hook成功,也无法区分是写到哪个物理磁盘(SSD缓存、RAID控制器、NVMe namespace都可能让逻辑写≠物理写)
- 性能开销极大:每次
read/write都要进你的代理函数,对高频I/O程序(如数据库)影响显著 - 多线程安全难保障:全局计数器需原子操作或锁,而系统调用本身已是瓶颈,再加同步进一步拖慢
- 真正需要程序级I/O追踪时,应改用eBPF(Linux)或ETW(Windows)——它们工作在内核层,无侵入且完整
实际部署时,最容易被忽略的是设备名动态性:云主机热插拔磁盘、容器运行时挂载新块设备、甚至LVM逻辑卷重映射,都会让预设的设备标识失效。建议在采集前先做一次设备发现(Linux查/sys/block/*/device/model,Windows查WMI Win32_DiskDrive),再绑定统计目标。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











