iostat命令详解_对iostat输出结果的理解-编程知识网

前言:

日常工作中,线上服务会出现各种奇奇怪怪的问题,每次出现问题都是根据现象猜测出现问题的原因,比如请求响应慢了,就排查整个请求的逻辑,每一步花了多少时间,分析半天终于发现是某一步慢了以后,在分析为什么慢。用这样的方式排查问题效率很低,而且很容易查错方向,导致不能及时解决问题。直到有一天,一位前辈指点道:出现问题不知道怎么回事,查看系统监控啊。。。这才恍然大悟。

于是我就想整理一下,有哪些值得关注的系统监控可以查看。这个帖子linux系统监控命令汇总给了我思路,可以从内存、cpu、硬盘、网络,四个方面对系统进行监控。于是我开始整理相关的命令,然后到了磁盘io的时候,查到了iostat命令。在这个帖子IO实时监控命令iostat详解中介绍iostat -x 输出的内容里,有一个avgqu-sz 是平均请求队列的长度。并且说,队列长度越短越好。然后我就不禁好奇,这个队列是什么呢?操作系统处理磁盘io的模型是什么呢?于是展开了这次学习之旅。

正文:

这次学习首先发现了之前的一些理解上的偏差,就是用户态内核态阻塞的关系:

用户态中进程执行read()方法,因为cpu的运算速度远大于磁盘的寻址速度,所以这个操作是阻塞的。之前我错误的理解是,用户进程执行read()方法,会导致进程阻塞,所以接下来会进入另一个进程,同时因为是系统调用,所以会进入内核态,所以,我理所当然的认为,内核态也是一个进程,比如有n个进程都调用read方法,这个时候内核进程就会把这些io请求放到一个队列里去调度。正好跟iostat中的队列相对应。

然鹅,这个观点是及其错误的,用户态和内核态是一个进程的两种状态,两种状态的区别是权限级别不一样,所以用户态进程执行read()方法,并不会发生上下文切换,只是改变了进程的特权等级。

那么问题就来了,进程什么时候阻塞的?队列又在哪里呢?后来就看到了这个帖子read系统调用深度剖析里边讲到,read内核态的操作是在不同层上处理的,比如我们要read()a.txt,表面上看,这是读取一个文件,算一个read请求,但是实际上,这个a.txt只是vfs的抽象出的概念,底层具体文件系统一直到设备驱动层,有可能就变成了对多个扇区的访问。所以iostat监控的请求数量,并不是我们进程中执行read的数量,而是具体对磁盘扇区操作请求的数量

接下来的问题就是,队列在哪里呢?比如有n个进程都要读写磁盘,这些进程会在一个队列里边排队,一个一个执行吗?这个逻辑明显是有漏洞的,如果一个进程读很大的文件,岂不是要把其他进程饿死。还好,发现了这个帖子CFQ完全公平队列,里边讲到,目前内核中默认的调度算法应该是cfq,叫做完全公平队列调度。这个调度算法人如其名,它试图给所有进程提供一个完全公平的IO操作环境。它为每个进程创建一个同步IO调度队列,并默认以时间片和请求数限定的方式分配IO资源。也就是说,对于每个进程的多个io操作,都会放到一个队列中。io调度层根据时间片调度这些请求。当某一个时间片该进程的数据读取完成了,方法返回,如果时间片内没有读取完,进程进入阻塞。

所以所谓的阻塞的系统调用,并不是调用后立刻就进入阻塞状态,而是进入内核态以后,在特定的条件下把进程阻塞掉。所以也就有了非阻塞的io调用,在自己的时间片内执行不完io操作,不进入阻塞而是直接返回失败。下次再调用的时候再执行一部分io操作,直到某一次调用把数据读取完了,返回成功。

另一个疑问是,文章提到:当设备完成了 IO 请求之后,通过中断的方式通知 cpu,比如某个进程将请求加入自己的队列以后,把队列中相邻的请求优化掉,然后对磁盘发起读请求,这个时候后续的读操作到底需不需要cpu参与呢?如果需要的话,感觉就不需要中断通知了。如果不需要的话,磁盘自己能把数据写入内存吗?

然后就查到了这个:硬盘属于外储存器,CPU只能直接读取内储存器,如果cpu只能读取内存的话,队列中对于磁盘扇区到读写操作就不会占用cpu资源。但是数据到达内存以后,复制到用户空间需要cpu参与,所以大量文件到读写还是会占用cpu资源。

总结一下,iostat有两个数据监控,一个是cpu,一个是硬盘,之所以这么设计,我的理解是把io操作分为了需要cpu和不需要cpu的两个阶段,可以针对两个阶段的监控信息分别分析问题,比如,如果硬盘监控的%util接近100%,说明磁盘一直在读写扇区,这个时候硬盘是瓶颈。如果硬盘正常,cpu彪高,说明大量内存的读写占用了大量cpu,这时cpu是瓶颈。

另外,因为cfq队列对每个进程都维护一个队列,所以iostat中avgqu-sz 是平均请求队列的长度,是所有队列的平均值。

补充一点的话,还看到这么个说法:

传统磁盘本质上一种机械装置,如FC,SAS,SATA磁盘,转速通常为5400/7200/10K/15K rpm不等。影响磁盘的关键因素是磁盘服务时间,即磁盘完成一个I/O请求所花费的时间,它由寻道时间、旋转延迟和数据传输时间三部分构成。寻道时间Tseek是指将读写磁头移动至正确的磁道上所需要的时间。寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3-15ms。旋转延迟Trotation是指盘片旋转将请求数据所在扇区移至读写磁头下方所需要的时间。旋转延迟取决于磁盘转速,通常使用磁盘旋转一周所需时间的1/2表示。比如,7200 rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms,而转速为15000 rpm的磁盘其平均旋转延迟约为2ms。数据传输时间Ttransfer是指完成传输所请求的数据所需要的时间,它取决于数据传输率,其值等于数据大小除以数据传输率。目前IDE/ATA能达到133MB/s,SATA II可达到300MB/s的接口数据传输率,数据传输时间通常远小于前两部分时间。因此,理论上可以计算出磁盘的最大IOPS,即IOPS = 1000 ms/ (Tseek + Troatation),忽略数据传输时间。假设磁盘平均物理寻道时间为3ms, 磁盘转速为7200,10K,15K rpm,则磁盘IOPS理论最大值分别为,IOPS = 1000 / (3 + 60000/7200/2) = 140IOPS = 1000 / (3 + 60000/10000/2) = 167IOPS = 1000 / (3 + 60000/15000/2) = 200

也就是说,磁盘瓶颈的本质是转速,10K的硬盘,每秒最多处理167个请求,如果iostat监控发现r/s超过了167,肯定会导致%util 100%以及很高的await,所以硬盘的瓶颈是请求次数。而cpu的瓶颈是访问的数据量,因为越大的数据量就需要越多的指令去复制拷贝。

总结:

最开始分析内存监控的时候,发现了cache和buffer区的概念,对磁盘的操作会把磁盘数据缓存到cache中,也就是说如果写一个测试用例不停的读写同一个文件,看监控并不会发现%util有变化,因为都是对内存的操作。

附录:

linux磁盘io调度算法:linux IO调度算法

最后,让我们保持独立思考,不卑不亢。长成自己想要的样子!
(引用自 我非常喜欢的B站up主 ”独立菌儿“的口头禅哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili)