如何提高Linux下块设备IO的整体性能?(3)
我们目前分析的内核代码版本为Linux 4.4,可以看出,从cfq的角度来说,已经可以实现异步IO的cgroup支持了,我们需要定义一下这里所谓异步IO的含义,它仅仅表示从内存的buffer/cache中的数据同步到硬盘的IO请求,而不是aio(man 7 aio)或者linux的native异步io以及libaio机制,实际上这些所谓的“异步”IO机制,在内核中都是同步实现的(本质上冯诺伊曼计算机没有真正的“异步”机制). 我们在上面已经说明过,由于进程正常情况下都是将数据先写入buffer/cache,所以这种异步IO都是统一由cfq_group中的async请求队列处理的. 那么为什么在上面的service_tree中还要实现和一个ASYNC的类型呢? 这当然是为了支持区分进程的异步IO并使之可以“完全公平”做准备喽. 实际上在最新的cgroup v2的blkio体系中,内核已经支持了针对buffer IO的cgroup限速支持,而以上这些可能容易混淆的一堆类型,都是在新的体系下需要用到的类型标记. 新体系的复杂度更高了,功能也更加强大,但是大家先不要着急,正式的cgroup v2体系,在Linux 4.5发布的时候会正式跟大家见面. 我们继续选择service_tree的过程,三种优先级类型的service_tree的选择就是根据类型的优先级来做选择的,RT优先级最高,BE其次,IDLE最低.就是说,RT里有,就会一直处理RT,RT没了再处理BE. 每个service_tree对应一个元素为cfq_queue排队的红黑树,而每个cfq_queue就是内核为进程(线程)创建的请求队列. 每一个cfq_queue都会维护一个rb_key的变量,这个变量实际上就是这个队列的IO服务时间(service time). 这里还是通过红黑树找到service time时间最短的那个cfq_queue进行服务,以保证“完全公平”. 选择好了cfq_queue之后,就要开始处理这个队列里的IO请求了.这里的调度方式基本跟deadline类似. cfq_queue会对进入队列的每一个请求进行两次入队,一个放进fifo中,另一个放进按访问扇区顺序作为key的红黑树中. 默认从红黑树中取请求进行处理,当请求的延时时间达到deadline时,就从红黑树中取等待时间最长的进行处理,以保证请求不被饿死. 这就是整个cfq的调度流程,当然其中还有很多细枝末节没有交代,比如合并处理以及顺序处理等等. 1.2 cfq的参数调整理解整个调度流程有助于我们决策如何调整cfq的相关参数.所有cfq的可调参数都可以在/sys/class/block/sda/queue/iosched/目录下找到,当然,在你的系统上,请将sda替换为相应的磁盘名称.我们来看一下都有什么: 这些参数部分是跟机械硬盘磁头寻道方式有关的,如果其说明你看不懂,请先补充相关知识: back_seek_max:磁头可以向后寻址的最大范围,默认值为16M. back_seek_penalty:向后寻址的惩罚系数.这个值是跟向前寻址进行比较的. 以上两个是为了防止磁头寻道发生抖动而导致寻址过慢而设置的.基本思路是这样,一个io请求到来的时候,cfq会根据其寻址位置预估一下其磁头寻道成本.
这两个参数实际上是cfq判断请求合并处理的条件限制,凡事复合这个条件的请求,都会尽量在本次请求处理的时候一起合并处理. fifo_expire_async:设置异步请求的超时时间. 同步请求和异步请求是区分不同队列处理的,cfq在调度的时候一般情况都会优先处理同步请求,之后再处理异步请求,除非异步请求符合上述合并处理的条件限制范围内. 当本进程的队列被调度时,cfq会优先检查是否有异步请求超时,就是超过fifo_expire_async参数的限制.如果有,则优先发送一个超时的请求,其余请求仍然按照优先级以及扇区编号大小来处理. fifo_expire_sync:这个参数跟上面的类似,区别是用来设置同步请求的超时时间. slice_idle:参数设置了一个等待时间.这让cfq在切换cfq_queue或service tree的时候等待一段时间,目的是提高机械硬盘的吞吐量. 一般情况下,来自同一个cfq_queue或者service tree的IO请求的寻址局部性更好,所以这样可以减少磁盘的寻址次数.这个值在机械硬盘上默认为非零. 当然在固态硬盘或者硬RAID设备上设置这个值为非零会降低存储的效率,因为固态硬盘没有磁头寻址这个概念,所以在这样的设备上应该设置为0,关闭此功能. group_idle:这个参数也跟上一个参数类似,区别是当cfq要切换cfq_group的时候会等待一段时间. 在cgroup的场景下,如果我们沿用slice_idle的方式,那么空转等待可能会在cgroup组内每个进程的cfq_queue切换时发生. 这样会如果这个进程一直有请求要处理的话,那么直到这个cgroup的配额被耗尽,同组中的其它进程也可能无法被调度到.这样会导致同组中的其它进程饿死而产生IO性能瓶颈. 在这种情况下,我们可以将slice_idle = 0而group_idle = 8.这样空转等待就是以cgroup为单位进行的,而不是以cfq_queue的进程为单位进行,以防止上述问题产生. low_latency:这个是用来开启或关闭cfq的低延时(low latency)模式的开关. 当这个开关打开时,cfq将会根据target_latency的参数设置来对每一个进程的分片时间(slice time)进行重新计算. 这将有利于对吞吐量的公平(默认是对时间片分配的公平). 关闭这个参数(设置为0)将忽略target_latency的值.这将使系统中的进程完全按照时间片方式进行IO资源分配.这个开关默认是打开的. 我们已经知道cfq设计上有“空转”(idling)这个概念,目的是为了可以让连续的读写操作尽可能多的合并处理,减少磁头的寻址操作以便增大吞吐量. (编辑:ASP站长网) |