内核很多重要子系统均通过proc文件的方式,将自身的一些统计信息输出,方便最终用户查看各子系统的运行状态,这些统计信息被称为metrics。 直接查看metrics并不能获取到有用的信息,一般都是由特定的应用程序(htop/sar/iostat等)每隔一段时间读取相关metrics,并进行相应计算,给出更具用户可读性的输出。 常见的metrics文件有:
• cpu调度统计信息的/proc/stat
• cpu负载统计信息的/proc/loadavg
通用块设备层也有一个重要的统计信息
• /proc/diskstats内核通过diskstats文件,将通用块设备层的一些重要指标以文件的形式呈现给用户
首先来看下diskstats里面都有些什么,下面截取的是一个diskstats文件内容:
虽然如上面提到的,这些数字看上去完全没有规律。不过若想研究内核通用块设备层的统计实现方式,还是得一个一个字段的分析。
# cat /proc/diskstats 2 0 fd0 0 0 0 0 0 0 0 0 0 0 0 11 0 sr0 18 0 2056 98 0 0 0 0 0 90 98 8 0 sda 5941 81 508270 54421 2051 3068 49868 15639 0 13973 69944 8 1 sda1 159 0 9916 304 11 2 68 28 0 292 332 8 2 sda2 5751 81 495010 54034 2040 3066 49800 15611 0 13834 69529 253 0 dm-0 5662 0 485850 54998 5106 0 49800 31524 0 13807 86522 253 1 dm-1 90 0 4920 156 0 0 0 0 0 123 156
/proc/diskstats有11(从F4开始)个字段,以下内核文档解释了它们的含义https://www.kernel.org/doc/Documentation/iostats.txt,我重新表述了一下,注意除了字段#9(F12)之外都是累计值,从系统启动之后一直累加:
1. (rd_ios)读操作的次数。
2. (rd_merges)合并读操作的次数。如果两个读操作读取相邻的数据块时,可以被合并成一个,以提高效率。合并的操作通常是I/O scheduler(也叫elevator)负责的。
3. (rd_sectors)读取的扇区数量。
4. (rd_ticks)读操作消耗的时间(以毫秒为单位)。每个读操作从__make_request()开始计时,到end_that_request_last()为止,包括了在队列中等待的时间。
5. (wr_ios)写操作的次数。
6. (wr_merges)合并写操作的次数。
7. (wr_sectors)写入的扇区数量。
8. (wr_ticks)写操作消耗的时间(以毫秒为单位)。
9. (in_flight)当前未完成的I/O数量。在I/O请求进入队列时该值加1,在I/O结束时该值减1。
注意:是I/O请求进入队列时,而不是提交给硬盘设备时。
10. (io_ticks)该设备用于处理I/O的自然时间(wall-clock time)。
请注意io_ticks与rd_ticks(字段#4)和wr_ticks(字段#8)的区别,rd_ticks和wr_ticks是把每一个I/O所消耗的时间累加在一起,因为硬盘设备通常可以并行处理多个I/O,所以rd_ticks和wr_ticks往往会比自然时间大。而io_ticks表示该设备有I/O(即非空闲)的时间,不考虑I/O有多少,只考虑有没有。在实际计算时,字段#9(in_flight)不为零的时候io_ticks保持计时,字段#9(in_flight)为零的时候io_ticks停止计时。
11. (time_in_queue)对字段#10(io_ticks)的加权值。字段#10(io_ticks)是自然时间,不考虑当前有几个I/O,而time_in_queue是用当前的I/O数量(即字段#9 in-flight)乘以自然时间。虽然该字段的名称是time_in_queue,但并不真的只是在队列中的时间,其中还包含了硬盘处理I/O的时间。iostat在计算avgqu-sz时会用到这个字段。
以sda磁盘为例: 8 0 sda 5941 81 508270 54421 2051 3068 49868 15639 0 13973 69944
域 | Value | Quoted | 解释 |
F1 | 8 | major number | 此块设备的主设备号 |
F2 | 0 | minor mumber | 此块设备的次设备号 |
F3 | sda | device name | 此块设备名字 |
F4 | 5941 | reads completed successfully | 成功完成的读请求次数 |
F5 | 81 | reads merged | 读请求的次数 |
F6 | 508270 | sectors read | 读请求的扇区数总和 |
F7 | 54421 | time spent reading (ms) | 读请求花费的时间总和 |
F8 | 2051 | writes completed | 成功完成的写请求次数 |
F9 | 3068 | writes merged | 写请求合并的次数 |
F10 | 49868 | sectors written | 写请求的扇区数总和 |
F11 | 15639 | time spent writing (ms) | 写请求花费的时间总和 |
F12 | 0 | I/Os currently in progress | 次块设备队列中的IO请求数 |
F13 | 13973 | time spent doing I/Os (ms) | 块设备队列非空时间总和 |
F14 | 69944 | weighted time spent doing I/Os (ms) | 块设备队列非空时间加权总和 |
流程图
下图是Linux内核通用块设备层IO请求处理的完整流程,如图例所示,所有的统计相关处理均有用不同颜色标注。 在进行深入分析前,请大致浏览图片,对整个流程有一个大致印象。
更详细的内容,请阅读:http://ykrocku.github.io/blog/2014/04/11/diskstats/