sudo dd if=/dev/sda of=/dev/null bs=1M iflag=direct
atopsar -d 5 # in a second terminal
top # in a third terminal
由于。。。导致的结果atopsar
:
19:18:32 disk busy read/s KB/read writ/s KB/writ avque avserv _dsk_
...
19:16:50 sda 18% 156.5 1024.0 0.0 0.0 5.0 1.15 ms
19:16:55 sda 18% 156.3 1024.0 0.0 0.0 4.9 1.15 ms
...
为什么报告的磁盘利用率(“繁忙”)远低于 100%?
据 称top
,该dd
进程仅使用 CPU 的 3% 或更少。 还提供系统 CPU 的硬件和软件中断 (和) 使用率top
的总体报告,显示低于 1%。我有四个 CPU(2 个核心,每个核心有 2 个线程)。hi
si
/dev/sda
是SATA硬盘。它不是 SSD,甚至不是混合 SSHD 驱动器。它的读取速度不能超过每秒 150 兆字节:-)。因此,这部分结果是有意义的:156 读取/秒 * 1024 KB/读取 = 156 MB/秒
内核版本是5.0.9-200.fc29.x86_64
(Fedora Workstation 29)。 IO调度器是mq-deadline
.从内核版本 5.0 开始,Fedora 使用多队列块层。因为单个队列块层已被删除:-)。
我相信 中的磁盘利用率数字atopsar -d
是atop
根据其中之一计算得出的内核 iostat 字段。链接的文档提到“字段 10 -- 执行 I/O 所花费的毫秒数”。还有一个更详细的定义,尽管我不确定它提到的函数是否仍然存在于多队列块层中。据我所知,和atopsar -d
都atop
使用通用代码sar -d
读取此字段 10.(我相信//也使用此iostat -x
字段mxiostat.py
)
附加测试
变体 2:更改为bs=512k
,但保留iflag=direct
.
dd if=/dev/sda of=/dev/null bs=512k iflag=direct
19:18:32 disk busy read/s KB/read writ/s KB/writ avque avserv _dsk_
...
19:18:00 sda 35% 314.0 512.0 0.0 0.0 2.1 1.12 ms
19:18:05 sda 35% 313.6 512.0 0.2 4.0 2.1 1.11 ms
变体 3:使用bs=1M
,但删除iflag=direct
.dd
使用大约 10% CPU 和 35% 磁盘。
dd if=/dev/sda of=/dev/null bs=1M
19:18:32 disk busy read/s KB/read writ/s KB/writ avque avserv _dsk_
...
19:21:47 sda 35% 242.3 660.2 0.0 0.0 5.4 1.44 ms
19:21:52 sda 31% 232.3 667.8 0.0 0.0 9.5 1.33 ms
如何重现这些结果 - 重要细节
注意最后一个测试,即运行dd
没有 iflag=direct
这有点像猪。我看到它冻结了系统(鼠标光标)十秒或更长时间。即使我禁用了交换。 (测试将您的 RAM 填满增益/缓存。它正在填充非活动 LRU 列表。我认为周转会相对较快地驱逐不活动的缓存页面。同时,磁盘正忙于顺序读取,因此当您需要将某些内容分页时,需要花费更长的时间。这种情况有多糟糕可能取决于内核最终是否也会翻转活动 LRU 列表,或者将其缩小太多。即当前的情况如何“混合了许多不同的算法,并进行了一些修改以捕捉极端情况和各种优化”在你的情况下工作)。
这精确的第一次测试的结果很难重现。
有时,KB/read
显示为512
而不是1024
。在这种情况下,其他结果看起来更像是 的结果bs=512k
。其中,它显示磁盘利用率约为 35%,而不是 20% 左右。无论哪种情况,我的问题都是成立的。
如果您想了解此行为,请参阅此处:为什么我的 IO 请求大小被限制为大约 512K?
答案1
这是内核版本 5.0 中的更改的结果:
块:删除part_round_stats和切换到不太精确的计数
我们想要转换为每个 cpu 的 in_flight 计数器。
函数part_round_stats每个jiffy都需要in_flight计数器,每个jiffy对所有percpu变量求和的成本太高,所以必须删除它。 part_round_stats 用于计算两个计数器 - time_in_queue 和 io_ticks。
time_in_queue 可以在没有part_round_stats 的情况下计算,通过添加I/O 结束时I/O 的持续时间(该值几乎与之前计算的值一样精确,除了不计算正在进行的I/O 的时间)。
io_ticks 可以通过在 I/O 开始或结束且 jiffies 值发生变化时增加该值来近似。如果 I/O 花费的时间少于一瞬间,则该值与之前计算的值一样精确。如果 I/O 花费的时间超过一瞬间,io_ticks 可能会漂移到先前计算的值之后。
(io_ticks
用于部分统计显示(),提供内核IO统计对于“字段 10 -- 执行 I/O 所花费的毫秒数”。)
这很好地解释了我的结果。在 Fedora 内核配置中,“瞬间“是 1 毫秒。我预计提交的大型读取 IOdd
可能会等待超过一两个 jiffies。特别是在我的系统上,它使用老式机械 HDD。
当我返回到之前的内核系列 4.20.x 时,它显示了正确的磁盘利用率:
$ uname -r
4.20.15-200.fc29.x86_64
$ atopsar -d 5
...
13:27:19 disk busy read/s KB/read writ/s KB/writ avque avserv _dsk_
13:28:49 sda 98% 149.4 1024.0 13.0 5.3 2.2 6.04 ms
13:28:54 sda 98% 146.0 1024.0 7.2 5.7 1.5 6.38 ms
cfq
这个旧内核默认使用传统的单队列块层和IO 调度程序。使用IO调度器时结果也是一样的deadline
。
更新:从内核 5.7 开始,这个近似值被调整。问题中的命令再次显示磁盘利用率为 100%。对于一些更复杂的工作负载,新的近似值预计会崩溃(尽管我还没有注意到)。
block/diskstats:慢速磁盘的 io_ticks 更准确的近似值
目前,如果 jiffies 计数器已更改,则 io_ticks 的近似值是在请求的每个开始和结束处加一。这对于短于一瞬间的请求或者其中一个请求在每一瞬间开始/结束的情况来说非常有效。
如果磁盘一次只执行一个请求,并且它们长于两个 jiffies,则仅计算第一个和最后一个 jiffies。
修复很简单:在请求结束时,将自上次更新以来传递的 io_ticks jiffy 添加到 io_ticks 中,而不仅仅是一个 jiffy。
示例:普通硬盘执行随机读取 4k 请求的时间约为 12ms。
fio --name=test --filename=/dev/sdb --rw=randread --direct=1 --runtime=30 &
iostat -x 10 sdb
注意补丁前后 iostat 的“%util”8,43% -> 99,99% 的变化:
前:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sdb 0,00 0,00 82,60 0,00 330,40 0,00 8,00 0,96 12,09 12,09 0,00 1,02 8,43
后:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sdb 0,00 0,00 82,50 0,00 330,00 0,00 8,00 1,00 12,10 12,10 0,00 12,12 99,99
现在 io_ticks 不会减少请求开始和结束之间的时间,但对于队列深度 > 1 ,相邻开始之间的一些 I/O 时间可能会丢失。
对于负载估计,“%util”不如平均队列长度有用,但它清楚地显示磁盘队列完全为空的频率。
修复:5b18b5a(“阻止:删除part_round_stats并切换到不太精确的计数”)
签署人:Konstantin Khlebnikov <[电子邮件受保护]>
审稿人:雷鸣 <[电子邮件受保护]>
签署人:Jens Axboe <[电子邮件受保护]>