I/O 统计字段¶
自 2.4.20(以及之前的一些版本,带有补丁)和 2.5.45 以来,引入了更广泛的磁盘统计信息,以帮助衡量磁盘活动。诸如 sar
和 iostat
之类的工具通常会解释这些并为您完成工作,但是如果您有兴趣创建自己的工具,则此处将解释这些字段。
在 2.4 中,该信息现在以 /proc/partitions
中的附加字段形式找到。在 2.6 及更高版本中,相同的信息在两个位置找到:一个在文件 /proc/diskstats
中,另一个在 sysfs 文件系统中,必须挂载该文件系统才能获取信息。在本文档中,我们假设 sysfs 挂载在 /sys
上,当然它也可以挂载在任何位置。 /proc/diskstats
和 sysfs 都使用相同的信息源,因此不应有所不同。
以下是这些不同格式的示例
2.4:
3 0 39082680 hda 446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160
3 1 9221278 hda1 35486 0 35496 38030 0 0 0 0 0 38030 38030
2.6+ sysfs:
446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160
35486 38030 38030 38030
2.6+ diskstats:
3 0 hda 446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160
3 1 hda1 35486 38030 38030 38030
4.18+ diskstats:
3 0 hda 446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160 0 0 0 0
在 2.4 上,您可能会执行 grep 'hda ' /proc/partitions
。在 2.6+ 上,您可以选择 cat /sys/block/hda/stat
或 grep 'hda ' /proc/diskstats
。
一个优于另一个的优点是,如果您正在监视一组已知的小型磁盘,则 sysfs 选择效果很好。如果您正在监视大量磁盘,则 /proc/diskstats
可能是更好的选择,因为您可以避免每次磁盘统计信息的快照都打开/关闭 50、100 或 500 个或更多开销。
在 2.4 中,统计字段是设备名称之后的字段。在上面的示例中,第一个统计信息字段将是 446216。相比之下,在 2.6+ 中,如果您查看 /sys/block/hda/stat
,您将只找到 15 个字段,从 446216 开始。如果您查看 /proc/diskstats
,则这 15 个字段的前面将是主设备号和次设备号以及设备名称。这些格式中的每一种都提供 15 个统计字段,每个字段的含义完全相同。除字段 9 外的所有字段都是自启动以来的累积值。字段 9 应在 I/O 完成后变为零;所有其他字段只会增加(除非它们溢出并回绕)。在非常繁忙或寿命很长的系统上,最终可能会发生回绕;因此,应用程序应准备好处理它。关于回绕,字段的类型为无符号 int(32 位)或无符号 long(32 位或 64 位,取决于您的机器),如下所述。除非您的观察在时间上分布非常广泛,否则这些字段不应在您注意到它之前回绕两次。
每组统计信息仅适用于指示的设备;如果您想要系统范围的统计信息,则必须找到所有设备并将它们全部加起来。
- 字段 1 -- 完成的读取次数(无符号 long)
这是成功完成的读取总数。
- 字段 2 -- 合并的读取次数,字段 6 -- 合并的写入次数(无符号 long)
彼此相邻的读取和写入可以合并以提高效率。因此,两个 4K 读取可能会变成一个 8K 读取,然后再最终传递给磁盘,因此它将仅被计数(和排队)为一个 I/O。此字段让您知道此操作的频率。
- 字段 3 -- 读取的扇区数(无符号 long)
这是成功读取的扇区总数。
- 字段 4 -- 读取所用的毫秒数(无符号 int)
这是所有读取花费的总毫秒数(从 blk_mq_alloc_request() 到 __blk_mq_end_request() 测量)。
- 字段 5 -- 完成的写入次数(无符号 long)
这是成功完成的写入总数。
- 字段 6 -- 合并的写入次数(无符号 long)
请参阅字段 2 的说明。
- 字段 7 -- 写入的扇区数(无符号 long)
这是成功写入的扇区总数。
- 字段 8 -- 写入所用的毫秒数(无符号 int)
这是所有写入花费的总毫秒数(从 blk_mq_alloc_request() 到 __blk_mq_end_request() 测量)。
- 字段 9 -- 当前正在进行的 I/O 数(无符号 int)
唯一应变为零的字段。当请求被给予适当的 struct request_queue 时递增,并在它们完成时递减。
- 字段 10 -- 执行 I/O 所用的毫秒数(无符号 int)
只要字段 9 非零,此字段就会增加。
自 5.0 以来,当至少有一个请求启动或完成时,此字段会计算 jiffies。如果请求运行时间超过 2 个 jiffies,则在并发请求的情况下,某些 I/O 时间可能未被考虑。
- 字段 11 -- 执行 I/O 所用的加权毫秒数(无符号 int)
此字段在每次 I/O 开始、I/O 完成、I/O 合并或读取这些统计信息时都会增加,增量值为正在进行的 I/O 数(字段 9)乘以自上次更新此字段以来执行 I/O 所用的毫秒数。这可以轻松衡量 I/O 完成时间和可能正在累积的积压。
- 字段 12 -- 完成的丢弃次数(无符号 long)
这是成功完成的丢弃总数。
- 字段 13 -- 合并的丢弃次数(无符号 long)
请参阅字段 2 的说明
- 字段 14 -- 丢弃的扇区数(无符号 long)
这是成功丢弃的扇区总数。
- 字段 15 -- 丢弃所用的毫秒数(无符号 int)
这是所有丢弃花费的总毫秒数(从 blk_mq_alloc_request() 到 __blk_mq_end_request() 测量)。
- 字段 16 -- 完成的刷新请求数
这是成功完成的刷新请求的总数。
块层组合刷新请求,并且一次最多执行一个。这将计数磁盘执行的刷新请求。不跟踪分区。
- 字段 17 -- 刷新所用的毫秒数
这是所有刷新请求所花费的总毫秒数。
为了避免引入性能瓶颈,在修改这些计数器时不会持有任何锁。这意味着当更改发生冲突时可能会引入细微的不准确性,因此(例如)每个分区发出的所有读取 I/O 的总和应等于对磁盘执行的读取 I/O 的总和...但由于缺少锁定,它可能只是非常接近。
在 2.6+ 中,每个 CPU 都有计数器,这使得缺少锁几乎不是问题。读取统计信息时,每个 CPU 的计数器会求和(可能会溢出它们求和到的无符号 long 变量),并将结果提供给用户。没有方便的用户界面来访问每个 CPU 的计数器本身。
自 4.19 以来,请求时间以纳秒精度测量,并在显示在此接口中之前截断为毫秒。
磁盘与分区¶
在 I/O 子系统中,2.4 和 2.6+ 之间存在重大变化。因此,一些统计信息消失了。从相对于分区的磁盘地址到相对于主机磁盘的磁盘地址的转换发生得更早。所有合并和计时现在都发生在磁盘级别,而不是像 2.4 中那样同时发生在磁盘级别和分区级别。因此,您将在 2.6+ 中看到分区与磁盘的统计信息输出不同。在 2.6+ 机器上,分区只有四个可用字段。这反映在上面的示例中。
- 字段 1 -- 发出的读取次数
这是为此分区发出的读取总数。
- 字段 2 -- 读取的扇区数
这是请求从该分区读取的扇区总数。
- 字段 3 -- 发出的写入次数
这是为此分区发出的写入总数。
- 字段 4 -- 写入的扇区数
这是请求写入此分区的扇区总数。
请注意,由于地址被转换为磁盘相对地址,并且没有保留分区相对地址的记录,因此后续读取的成功或失败不能归因于该分区。换句话说,分区的读取次数是在排队等待分区之前稍早计数,而整个磁盘的读取次数则是在完成时计数。这是一个细微的区别,在大多数情况下可能并不重要。
更重要的是,在合并之前计算分区的读取/写入次数,而在合并之后计算磁盘的读取/写入次数所引起的误差。由于典型的工作负载通常包含大量连续且相邻的请求,因此发出的读取/写入次数可能比完成的读取/写入次数高出数倍。
在 2.6.25 版本中,完整的统计信息集再次可用于分区和磁盘,并且分区统计信息再次一致。由于我们仍然不保留分区相对地址的记录,因此操作会归因于包含请求的第一个扇区的分区(在最终合并之后)。由于请求可以跨分区合并,这可能会导致一些(可能不重要的)不准确性。
其他注意事项¶
在 2.6+ 版本中,默认情况下不挂载 sysfs。如果您的 Linux 发行版尚未添加它,则您需要将其添加到 /etc/fstab
中,如下所示:
none /sys sysfs defaults 0 0
在 2.6+ 版本中,所有磁盘统计信息都已从 /proc/stat
中删除。在 2.4 版本中,它们同时出现在 /proc/partitions
和 /proc/stat
中,尽管 /proc/stat
中的格式与 /proc/partitions
中的格式非常不同(如果您的系统有 proc(5),请参阅它)。