I/O 统计字段¶
内核通过 /proc/diskstats
和 /sys/block/<device>/stat
公开磁盘统计信息。这些统计信息通常通过 sar
和 iostat
等工具访问。
以下是使用具有两个分区的磁盘的示例
/proc/diskstats:
259 0 nvme0n1 255999 814 12369153 47919 996852 81 36123024 425995 0 301795 580470 0 0 0 0 60602 106555
259 1 nvme0n1p1 492 813 17572 96 848 81 108288 210 0 76 307 0 0 0 0 0 0
259 2 nvme0n1p2 255401 1 12343477 47799 996004 0 36014736 425784 0 344336 473584 0 0 0 0 0 0
/sys/block/nvme0n1/stat:
255999 814 12369153 47919 996858 81 36123056 426009 0 301809 580491 0 0 0 0 60605 106562
/sys/block/nvme0n1/nvme0n1p1/stat:
492 813 17572 96 848 81 108288 210 0 76 307 0 0 0 0 0 0
这两个文件包含相同的 17 个统计信息。/sys/block/<device>/stat
包含 <device>
的字段。在 /proc/diskstats
中,这些字段以主设备号和次设备号以及设备名称为前缀。在上面的示例中,nvme0n1
的第一个统计值在两个文件中都是 255999。
sysfs stat
文件对于监控少量已知的磁盘非常有效。 如果您正在跟踪大量设备,则 /proc/diskstats
通常是更好的选择,因为它避免了为每个快照打开和关闭多个文件的开销。
除了字段 9 之外,所有字段都是累积的、单调递增的计数器,字段 9 在 I/O 完成时重置为零。 其余字段在启动时、设备重新连接或重新初始化时,或者当底层计数器溢出时重置。 读取这些计数器的应用程序在比较统计快照时应检测并处理重置。
每组统计信息仅适用于指示的设备; 如果您想要系统范围的统计信息,您必须找到所有设备并将它们全部加起来。
- 字段 1 -- 完成的读取次数 (unsigned long)
这是成功完成的读取总数。
- 字段 2 -- 合并的读取次数,字段 6 -- 合并的写入次数 (unsigned long)
彼此相邻的读取和写入可以合并以提高效率。 因此,两个 4K 读取可能会变成一个 8K 读取,然后再最终交给磁盘,因此它将被计为(和排队)只有一个 I/O。 此字段让您知道这种情况发生的频率。
- 字段 3 -- 读取的扇区数 (unsigned long)
这是成功读取的扇区总数。
- 字段 4 -- 读取所花费的毫秒数 (unsigned int)
这是所有读取花费的总毫秒数(从 blk_mq_alloc_request() 到 __blk_mq_end_request() 测量)。
- 字段 5 -- 完成的写入次数 (unsigned long)
这是成功完成的写入总数。
- 字段 6 -- 合并的写入次数 (unsigned long)
请参阅字段 2 的描述。
- 字段 7 -- 写入的扇区数 (unsigned long)
这是成功写入的扇区总数。
- 字段 8 -- 写入所花费的毫秒数 (unsigned int)
这是所有写入花费的总毫秒数(从 blk_mq_alloc_request() 到 __blk_mq_end_request() 测量)。
- 字段 9 -- 当前正在进行的 I/O 数 (unsigned int)
唯一应该变为零的字段。 当请求被赋予适当的 struct request_queue 时递增,并在完成时递减。
- 字段 10 -- 执行 I/O 所花费的毫秒数 (unsigned int)
只要字段 9 不为零,此字段就会增加。
自 5.0 起,此字段在至少一个请求已启动或完成时计算节拍数。 如果请求运行超过 2 个节拍,则在并发请求的情况下,某些 I/O 时间可能不会被计算在内。
- 字段 11 -- 执行 I/O 所花费的加权毫秒数 (unsigned int)
每次 I/O 开始、I/O 完成、I/O 合并或读取这些统计信息时,此字段都会增加,增加的值为正在进行的 I/O 数(字段 9)乘以自上次更新此字段以来执行 I/O 所花费的毫秒数。 这可以轻松地衡量 I/O 完成时间和可能正在累积的积压。
- 字段 12 -- 完成的丢弃次数 (unsigned long)
这是成功完成的丢弃总数。
- 字段 13 -- 合并的丢弃次数 (unsigned long)
请参阅字段 2 的描述
- 字段 14 -- 丢弃的扇区数 (unsigned long)
这是成功丢弃的扇区总数。
- 字段 15 -- 丢弃所花费的毫秒数 (unsigned int)
这是所有丢弃花费的总毫秒数(从 blk_mq_alloc_request() 到 __blk_mq_end_request() 测量)。
- 字段 16 -- 完成的刷新请求数
这是成功完成的刷新请求总数。
块层结合了刷新请求,并且一次最多执行一个。 这计算由磁盘执行的刷新请求。 不跟踪分区。
- 字段 17 -- 刷新所花费的毫秒数
这是所有刷新请求花费的总毫秒数。
为避免引入性能瓶颈,在修改这些计数器时不会持有任何锁。 这意味着当更改发生冲突时可能会引入微小的不准确性,因此(例如)将每个分区发出的所有读取 I/O 加起来应该等于对磁盘进行的读取 I/O ... 但是由于缺少锁定,它可能只是非常接近。
在 2.6+ 中,每个 CPU 都有计数器,这使得缺少锁定几乎不是问题。 读取统计信息时,每个 CPU 的计数器会被求和(可能会溢出求和到的无符号长变量),并将结果提供给用户。 没有方便的用户界面可以访问每个 CPU 的计数器本身。
自 4.19 起,请求时间以纳秒精度测量,并在显示在此接口中之前截断为毫秒。
磁盘与分区¶
在 2.4 和 2.6+ 之间的 I/O 子系统中存在重大变化。 因此,一些统计信息消失了。 从相对于分区的磁盘地址到相对于主磁盘的磁盘地址的转换发生得更早。 现在所有合并和计时都在磁盘级别而不是像 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),请参阅它。)