05.从零带你学习Linux内核:/proc
/proc/diskstats
这一节讲磁盘 I/O。核心问题是:
磁盘吞吐、IOPS、磁盘忙不忙,为什么也要靠两次采样差分?
你前面学过:
/proc/stat -> CPU 累计时间,要两次采样
/proc/loadavg -> 系统负载
/proc/meminfo -> 当前内存状态
现在 /proc/diskstats 和 /proc/stat 更像:它里面很多字段也是从开机到现在的累计值,所以要两次采样做差。
1. /proc/diskstats 是什么
一句话:
/proc/diskstats是 Linux 内核暴露的每块磁盘设备 I/O 统计信息,记录磁盘从系统启动以来完成了多少读写、读写了多少扇区、花了多少时间等。
两篇文章里都提到了 /proc/diskstats:一篇说它是“每块磁盘设备的磁盘 I/O 统计信息列表”,另一篇直接给了 cat /proc/diskstats 的输出示例。
2. 先看一行输出
执行:
cat /proc/diskstats
你可能看到类似:
8 0 sda 62475 31565 6616608 625859 666982 265602 28370898 2977046 0 666856 2741276 0 0 0 0
8 1 sda1 276 0 21216 2990 2 0 2 0 0 444 2736 0 0 0 0
8 5 sda5 61752 31565 6577386 622212 633817 265602 28370896 2928366 0 662648 2695364 0 0 0 0
先不要被一堆数字吓住。你第一阶段只需要看:
设备名
读完成次数
读扇区数
写完成次数
写扇区数
I/O耗时
比如 sda 是整块磁盘,sda1、sda5 是分区。做整盘监控时,一般看 sda、vda、nvme0n1 这种整盘设备,不优先看单个分区。
3. 常用字段怎么理解
简化看,一行大概是:
major minor name reads_completed reads_merged sectors_read time_reading
writes_completed writes_merged sectors_written time_writing
io_in_progress time_doing_io weighted_time_doing_io
你先记这几个:
| 字段 | 小白解释 | 可以算什么 |
|---|---|---|
name | 磁盘设备名,比如 sda、vda、nvme0n1 | 区分设备 |
reads_completed | 从开机以来完成了多少次读 I/O | 读 IOPS |
sectors_read | 从开机以来读了多少扇区 | 读吞吐 |
time_reading | 读操作累计耗时,通常是毫秒 | 读延迟参考 |
writes_completed | 从开机以来完成了多少次写 I/O | 写 IOPS |
sectors_written | 从开机以来写了多少扇区 | 写吞吐 |
time_writing | 写操作累计耗时,通常是毫秒 | 写延迟参考 |
io_in_progress | 当前正在进行的 I/O 数 | 当前队列压力 |
time_doing_io | 磁盘忙于 I/O 的累计时间 | 磁盘繁忙度 |
weighted_time_doing_io | 加权 I/O 时间 | 平均队列长度相关 |
第一阶段不用把全部字段背死,重点是:完成次数、扇区数、耗时。
4. 为什么 diskstats 也要两次采样
假设你读到:
reads_completed = 100000
sectors_read = 8000000
这不是“当前每秒读了多少”。
它表示:
从系统启动到现在
一共完成了 100000 次读 I/O
一共读了 8000000 个扇区
所以监控系统要做:
第一次读取 /proc/diskstats
↓
等待 1 秒或 10 秒
↓
第二次读取 /proc/diskstats
↓
第二次累计值 - 第一次累计值
↓
得到采样窗口内的读写次数和读写字节数
这和 CPU 使用率的思想一样:
累计值本身没意义
差分才代表这一段时间发生了什么
5. 怎么算磁盘吞吐
磁盘吞吐就是:
每秒读/写了多少字节
关键字段:
sectors_read
sectors_written
Linux 里传统扇区大小通常按 512 字节计算。
所以:
read_bytes_delta = (sectors_read_now - sectors_read_prev) * 512
write_bytes_delta = (sectors_written_now - sectors_written_prev) * 512
如果采样间隔是 interval_seconds:
read_Bps = read_bytes_delta / interval_seconds
write_Bps = write_bytes_delta / interval_seconds
换成 MB/s:
read_MBps = read_Bps / 1024 / 1024
write_MBps = write_Bps / 1024 / 1024
6. 手算一个吞吐例子
第一次采样:
sectors_read = 100000
sectors_written = 200000
第二次采样,间隔 1 秒:
sectors_read = 102000
sectors_written = 205000
差分:
read_sectors_delta = 102000 - 100000 = 2000
write_sectors_delta = 205000 - 200000 = 5000
换算字节:
read_bytes = 2000 * 512 = 1,024,000 B
write_bytes = 5000 * 512 = 2,560,000 B
所以:
read ≈ 0.98 MB/s
write ≈ 2.44 MB/s
这就是磁盘读写吞吐。
7. 怎么算 IOPS
IOPS 是:
每秒完成了多少次 I/O 操作。
关键字段:
reads_completed
writes_completed
公式:
read_iops = (reads_completed_now - reads_completed_prev) / interval_seconds
write_iops = (writes_completed_now - writes_completed_prev) / interval_seconds
例子:
第一次:
reads_completed = 1000
writes_completed = 2000
第二次,间隔 1 秒:
reads_completed = 1050
writes_completed = 2080
那么:
read_iops = 50
write_iops = 80
说明这一秒完成了 50 次读 I/O、80 次写 I/O。
8. 吞吐和 IOPS 有什么区别
这两个指标经常一起看。
| 指标 | 看什么 | 例子 |
|---|---|---|
| 吞吐 | 每秒搬了多少数据 | 100 MB/s |
| IOPS | 每秒做了多少次 I/O | 5000 次/s |
区别:
大文件顺序读写:
吞吐高,IOPS 未必特别高
大量小文件随机读写:
IOPS 高,吞吐未必高
小白类比:
吞吐 = 一秒搬了多少箱货
IOPS = 一秒跑了多少趟车
一趟车装得多,吞吐就高;一趟车装得少但跑得频繁,IOPS 就高。
9. 怎么粗略看磁盘忙不忙
关键字段:
time_doing_io
它表示磁盘忙于处理 I/O 的累计时间,通常单位是毫秒。
两次采样:
io_time_delta = time_doing_io_now - time_doing_io_prev
如果采样间隔是 1 秒,也就是 1000 ms:
util = io_time_delta / (interval_seconds * 1000)
例子:
1 秒内 time_doing_io 增加了 800 ms
则:
disk_util = 800 / 1000 = 80%
意思是:
这一秒里,磁盘大约有 80% 的时间在处理 I/O。
注意:这个值只能粗略理解成磁盘繁忙度。现代 SSD/NVMe 有并行队列,多队列设备上不能机械地等同于“磁盘一定 80% 饱和”,但面试第一轮这样讲够用。
10. 怎么粗略看读写延迟
可以用:
time_reading / reads_completed
time_writing / writes_completed
但也要做差分。
读平均耗时:
avg_read_latency_ms =
(time_reading_now - time_reading_prev) /
(reads_completed_now - reads_completed_prev)
写平均耗时:
avg_write_latency_ms =
(time_writing_now - time_writing_prev) /
(writes_completed_now - writes_completed_prev)
例子:
1 秒内完成 100 次读
读累计耗时增加 500 ms
则:
avg_read_latency = 500 / 100 = 5 ms
注意边界:
如果 reads_completed_delta = 0,就不能除,要避免除零。
11. 和 /proc/stat、/proc/loadavg 怎么联动
现在你已经有三类指标:
/proc/stat -> CPU 使用率、iowait
/proc/loadavg -> 系统任务积压
/proc/diskstats -> 磁盘吞吐、IOPS、繁忙度、延迟
常见诊断:
| 现象 | 可能原因 |
|---|---|
| load 高,CPU 不高,iowait 高,disk util 高 | 磁盘 I/O 瓶颈 |
| read/write MB/s 高,但 IOPS 不高 | 大块顺序读写 |
| IOPS 高,但 MB/s 不高 | 小块随机 I/O |
| Dirty/Writeback 高,write util 高 | 写回压力大 |
| CPU 高,disk util 低 | 主要是计算压力,不是磁盘 |
| load 高,disk util 不高,procs_running 高 | 可能是 CPU 调度压力或线程过多 |
你可以把它理解成:
loadavg 告诉你有没有排队
stat 告诉你 CPU 是否在等 I/O
diskstats 告诉你磁盘自己到底忙不忙
12. 放到你的性能监控项目里
你的项目本质是 Worker 采集 CPU、内存、磁盘、网络、软中断等指标,Manager 接收后计算健康分、写 MySQL 并提供查询接口;项目资料里也明确 Worker 端有多个采集模块,包含 disk 监控模块,典型采集方式包括磁盘容量、使用率和 I/O 相关指标。
磁盘采集链路可以这样讲:
Worker 定时读取 /proc/diskstats
↓
解析目标磁盘的 reads_completed / sectors_read / writes_completed / sectors_written / io_time
↓
保存上一轮采样值
↓
下一轮采样做差分
↓
计算 read_MBps / write_MBps / read_iops / write_iops / disk_util
↓
MetricCollector 聚合到 MonitorInfo
↓
MonitorPusher 通过 gRPC Push 到 Manager
↓
HostManager 纳入健康评分和变化率
↓
MySQL 落库,QueryService 查询趋势
这部分和项目主链路一致:Worker 采集指标,MetricCollector 聚合,封装成 Protobuf MonitorInfo,通过 gRPC Push 到 Manager,再由 HostManager 评分、落库、查询。
13. 面试官最可能问什么
13.1 磁盘吞吐怎么计算?
答:
/proc/diskstats里的sectors_read和sectors_written是从开机以来的累计扇区数,不是当前速率。监控程序需要两次采样做差,得到采样窗口内读写的扇区数,再乘以扇区大小,通常按 512 字节换算成字节数,最后除以采样间隔,得到 read/write Bps 或 MB/s。
13.2 IOPS 怎么计算?
答:
IOPS 看的是单位时间内完成了多少次 I/O。可以用
/proc/diskstats中的reads_completed和writes_completed做两次采样差分,再除以采样间隔,分别得到 read_iops 和 write_iops。
13.3 iowait 高一定是磁盘问题吗?
答:
不一定。
iowait表示 CPU 等待 I/O 的时间,磁盘慢是常见原因,但也可能是网络块存储、文件系统、设备驱动或其他块设备导致。要结合/proc/diskstats的磁盘 util、读写延迟、吞吐、IOPS,以及 load average 和阻塞进程数一起判断。
13.4 为什么磁盘指标也要两次采样?
答:
因为
/proc/diskstats中很多关键字段是累计值,例如完成的读写次数、读写扇区数、读写耗时。单次读取只能知道从开机以来总共发生了多少,不能代表当前速率。两次采样做差,才能得到采样窗口内的吞吐、IOPS 和磁盘繁忙度。
14. 小白最小记忆版
背这个:
/proc/diskstats 看每块磁盘的 I/O 统计。
它里面很多字段是开机以来的累计值,不是当前速率。
磁盘吞吐:
sectors_delta * 512 / interval
IOPS:
completed_delta / interval
磁盘繁忙度:
io_time_delta / (interval * 1000)
load 高 + CPU 不高 + iowait 高 + disk util 高
通常说明磁盘或块 I/O 是瓶颈。
15. 面试版一句话
/proc/diskstats记录每块磁盘从系统启动以来的 I/O 累计统计,比如完成的读写次数、读写扇区数、读写耗时和 I/O busy time。监控程序不能只读一次,而是要保存上一轮采样值,下一轮做差分。读写吞吐可以用扇区数差值乘以 512 再除以采样间隔,IOPS 可以用完成次数差值除以采样间隔,磁盘繁忙度可以用 I/O busy time 的增量除以采样窗口。我的项目里 Worker 会读取磁盘 I/O 指标,计算后聚合到 MonitorInfo,上报给 Manager 做健康评分和历史趋势分析。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)