/proc/diskstats 是 Linux 下获取磁盘 I/O 累计字节数最轻量可靠的方式,通过第6、10列扇区数乘512得字节数;Windows 需用 PDH API 查询 Bytes Read/Write/sec 并积分估算;/proc/[pid]/io 不反映真实磁盘字节,仅统计 page cache 操作。

Linux 下用 /proc/diskstats 解析磁盘 I/O 累计字节数
直接读取 /proc/diskstats 是最轻量、最可靠的方式。它不依赖外部工具或 root 权限,每行代表一个块设备,字段含义稳定(内核文档明确约定),且包含“累计读取扇区数”和“累计写入扇区数”,乘以 512 即得字节数。
注意:该文件反映的是**全系统级累计值**,不是当前进程独占的 I/O;C++ 程序无法直接获取“本进程磁盘读写字节数”,Linux 内核不向用户态暴露 per-process 的磁盘字节级统计(/proc/[pid]/io 只有 read_bytes/write_bytes,但它是**所有 I/O 类型汇总**,含缓存页回写、swap、甚至某些 direct I/O 可能不计入——实际测下来常比 diskstats 差异很大)。
实操建议:
- 按设备名(如
sda、nvme0n1)过滤/proc/diskstats行,跳过分区行(第 3 列非数字) - 第 6 列是“读取完成的扇区总数”,第 10 列是“写入完成的扇区总数”(字段索引从 1 开始)
- 每次采样后与上一次做差,再除以时间间隔,得到 B/s;别忘了乘 512
- 用
std::ifstream逐行读,std::istringstream拆字段,避免正则——开销大且易错
// 示例:提取 sda 的累计读写字节数
std::ifstream f("/proc/diskstats");
std::string line;
while (std::getline(f, line)) {
std::istringstream iss(line);
std::string major, minor, name;
iss >> major >> minor >> name;
if (name == "sda") {
long long reads_sectors, writes_sectors;
// 跳过前 5 字段,读第 6 和第 10
for (int i = 0; i < 5; ++i) iss >> std::ws;
iss >> reads_sectors;
for (int i = 0; i < 3; ++i) iss >> std::ws;
iss >> writes_sectors;
uint64_t total_read_bytes = reads_sectors * 512;
uint64_t total_write_bytes = writes_sectors * 512;
break;
}
}
Windows 下用 PdhAddCounter 获取磁盘总字节/秒(非累计)
Windows 没有等价于 /proc/diskstats 的文本接口。必须用 PDH(Performance Data Helper)API 查询性能计数器,其中 PhysicalDisk(*)Bytes Read/sec 和 PhysicalDisk(*)Bytes Written/sec 是最接近的指标。
立即学习“C++免费学习笔记(深入)”;
关键限制:它返回的是**滑动窗口平均速率(单位:B/s)**,不是累计值;也没有“自启动以来总字节数”。若硬要累计,只能自己积分(rate × time),但会因采样抖动累积误差。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
实操建议:
- 计数器路径用
\%s\Bytes Read/sec格式,%s填_Total或具体盘符(如C:),注意双反斜杠转义 - 必须调用
PdhCollectQueryData至少两次,间隔 ≥1 秒,第二次才返回有效值(首次为 0) - 不要用
PdhGetFormattedCounterValue直接取PDH_FMT_LONG,应取PDH_FMT_DOUBLE防溢出(尤其高吞吐场景) - 初始化需
PdhOpenQuery→PdhAddCounter→PdhCollectQueryData,漏任一环都会返回PDH_INVALID_HANDLE
为什么不能用 /proc/[pid]/io 算“本程序磁盘字节”
很多人第一反应是读 /proc/[pid]/io 里的 read_bytes 和 write_bytes,但它统计的是进程对**page cache 的读写总量**,不是真实发往磁盘的字节数。典型偏差场景:
- 重复读同一文件:page cache 命中,
read_bytes增加,但磁盘无读操作 - 写入后未
sync:数据仍在 page cache,write_bytes已计,但磁盘尚未写入 - 内存映射文件(
mmap)修改:可能完全不触发read_bytes/write_bytes计数 -
dd if=/dev/zero of=file bs=1M count=100后立即看/proc/[pid]/io,常发现write_bytes ≈ 100MB,但/proc/diskstats显示磁盘只写了几 MB(因延迟写入)
所以它适合估算进程 I/O 活跃度,但不能替代磁盘硬件层的字节统计。
动态监控时差分计算的陷阱
无论 Linux 还是 Windows,要算“每秒字节数”,本质是 (current - previous) / (time_elapsed)。这里容易踩三个坑:
- 采样间隔太短(如 100ms):Linux
/proc/diskstats更新有最小粒度(通常 100–500ms),高频读可能拿到重复值,算出 0 - 整数溢出:扇区数是
unsigned long long,两次差值仍可能超int64_t,建议全程用uint64_t存储累计值 - 时间测量不准:用
std::chrono::steady_clock,别用system_clock(可能被 NTP 调整) - 设备热插拔:若监控的磁盘被卸载,下次读
/proc/diskstats可能找不到该行,需容错并重置 baseline
真正可靠的“动态明细”,必须接受它是近似值——内核本身也不保证微秒级精确采样,重点是保持采样逻辑一致、间隔合理(推荐 1–5 秒),并清楚每个数字背后的统计口径。

















