直接读取 /proc/diskstats 是最轻量可靠的方式,单位为扇区(512字节),每行对应一设备,需按字段序号解析:第3列为设备名,第6列为已读扇区数,第10列为已写扇区数。

Linux 下用 /proc/diskstats 解析磁盘 I/O 累计字节数
直接读取 /proc/diskstats 是最轻量、最可靠的方式。它不依赖外部工具或特权,只要程序有读取权限就能拿到原始累计值(单位:扇区,512 字节)。注意:该文件每行对应一个设备,需按字段顺序解析,不能靠字段名匹配。
关键字段位置(以第 4 行为例):
– 第 3 列是设备名(如 sda)
– 第 6 列是已读扇区数(read_sectors)
– 第 10 列是已写扇区数(
)
- 每次读取后需缓存上一次的值,用差值除以时间间隔算出 B/s;不要直接用单次值做速率判断
- 过滤虚拟设备(如
loop、<code>zram、nvme0n1p1这类分区要回溯到nvme0n1才是物理设备) - 扇区大小固定为 512 字节,所以字节数 = 扇区数 × 512;别误用
getconf PAGESIZE或其他值
C++ 读取并解析 /proc/diskstats 的最小可行代码
用 std::ifstream 逐行读,std::istringstream 拆字段。避免用 system("cat ...") 或 popen——开销大且难同步。
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct DiskIO {
std::string name;
uint64_t read_sectors = 0;
uint64_t write_sectors = 0;
};
std::vector<diskio> read_diskstats() {
std::vector<diskio> result;
std::ifstream f("/proc/diskstats");
std::string line;
while (std::getline(f, line)) {
std::istringstream iss(line);
std::string major, minor, name;
uint64_t rd_ios, rd_merges, rd_sectors, rd_ticks;
uint64_t wr_ios, wr_merges, wr_sectors, wr_ticks;
uint64_t ios_pgr, tot_ticks, rq_ticks;
if (!(iss >> major >> minor >> name >> rd_ios >> rd_merges >>
rd_sectors >> rd_ticks >> wr_ios >> wr_merges >>
wr_sectors >> wr_ticks >> ios_pgr >> tot_ticks >> rq_ticks)) {
continue;
}
// 跳过分区行(含'p'或数字后缀),只保留主设备(如 sda, nvme0n1)
if (name.find('0') != std::string::npos || name.find('p') != std::string::npos) continue;
result.push_back({name, rd_sectors, wr_sectors});
}
return result;
}
</diskio></diskio></vector></string></sstream></fstream>
上面代码跳过了分区(如 sda1),只保留主设备。实际中若需统计某分区,得先从 /proc/mounts 或 lsblk 映射关系查出其对应主设备,再定位扇区偏移——但累计值本身仍来自主设备行。
计算每秒字节数时必须处理的三个陷阱
单纯相减再除以时间,大概率得到错误结果。常见原因不是代码写错,而是没理解数据源特性:
-
/proc/diskstats的值是**自系统启动以来的单调递增累计值**,但内核可能因设备重置、驱动 reload 等清零部分字段(极少见但存在),需检测突降(如本次读值比上次小)并忽略该次差值 - 两次采样间隔太短(
- 多个磁盘并发时,不能共用一个时间戳计算所有设备——要用
clock_gettime(CLOCK_MONOTONIC, &ts)在每次read_diskstats()前后分别打点,否则误差会累积
macOS 和 Windows 不支持 /proc/diskstats,怎么办
没有跨平台替代方案。macOS 必须用 IOKit 查询 kIOBlockStorageDeviceStatisticsKey,Windows 必须调 PdhAddCounter 订阅 \PhysicalDisk(*)\Disk Read Bytes/sec。二者都需要链接额外库、声明权限、处理异步回调,且返回的是瞬时速率而非“累计值推导”,底层机制完全不同。
如果你的程序必须跨平台,不要试图封装统一接口——在 CMake 中按 if(APPLE) / if(WIN32) 分支实现两套逻辑,各自用原生方式拿数据。强行抽象只会让错误更隐蔽,比如 Windows 下忘记调 PdhCollectQueryData 就永远返回 0。
真正容易被忽略的,是不同内核版本对 /proc/diskstats 字段顺序的微小调整(如 5.15+ 新增了 14 列),硬编码字段索引比用正则匹配更稳;还有 SSD 的 NVMe 设备名可能是 nvme0n1,但它的扇区统计依然在 nvme0n1 行,不在 nvme0 ——名字带数字不等于它是分区。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











