设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 手机 数据 公司
当前位置: 首页 > 服务器 > 安全 > 正文

如何提高Linux下块设备IO的整体性能?(4)

发布时间:2021-01-04 22:01 所属栏目:53 来源:网络整理
导读:如果有进程总是很快的进行顺序读写,那么它将因为cfq的空转等待命中率很高而导致其它需要处理IO的进程响应速度下降,如果另一个需要调度的进程不会发出大量顺序IO行为的话,系统中不同进程IO吞吐量的表现就会很不均衡.

如果有进程总是很快的进行顺序读写,那么它将因为cfq的空转等待命中率很高而导致其它需要处理IO的进程响应速度下降,如果另一个需要调度的进程不会发出大量顺序IO行为的话,系统中不同进程IO吞吐量的表现就会很不均衡.

就比如,系统内存的cache中有很多脏页要写回时,桌面又要打开一个浏览器进行操作,这时脏页写回的后台行为就很可能会大量命中空转时间,而导致浏览器的小量IO一直等待,让用户感觉浏览器运行响应速度变慢.

这个low_latency主要是对这种情况进行优化的选项,当其打开时,系统会根据target_latency的配置对因为命中空转而大量占用IO吞吐量的进程进行限制,以达到不同进程IO占用的吞吐量的相对均衡.这个开关比较合适在类似桌面应用的场景下打开.

target_latency:当low_latency的值为开启状态时,cfq将根据这个值重新计算每个进程分配的IO时间片长度.

quantum:这个参数用来设置每次从cfq_queue中处理多少个IO请求.在一个队列处理事件周期中,超过这个数字的IO请求将不会被处理.这个参数只对同步的请求有效.

slice_sync:当一个cfq_queue队列被调度处理时,它可以被分配的处理总时间是通过这个值来作为一个计算参数指定的.公式为:time_slice = slice_sync + (slice_sync/5 * (4 – prio)).这个参数对同步请求有效.

slice_async:这个值跟上一个类似,区别是对异步请求有效.

slice_async_rq:这个参数用来限制在一个slice的时间范围内,一个队列最多可以处理的异步请求个数.请求被处理的最大个数还跟相关进程被设置的io优先级有关.

1.3 cfq的IOPS模式

我们已经知道,默认情况下cfq是以时间片方式支持的带优先级的调度来保证IO资源占用的公平.

高优先级的进程将得到更多的时间片长度,而低优先级的进程时间片相对较小.

当我们的存储是一个高速并且支持NCQ(原生指令队列)的设备的时候,我们最好可以让其可以从多个cfq队列中处理多路的请求,以便提升NCQ的利用率.

此时使用时间片的分配方式分配资源就显得不合时宜了,因为基于时间片的分配,同一时刻最多能处理的请求队列只有一个.

这时,我们需要切换cfq的模式为IOPS模式.切换方式很简单,就是将slice_idle=0即可.内核会自动检测你的存储设备是否支持NCQ,如果支持的话cfq会自动切换为IOPS模式.

另外,在默认的基于优先级的时间片方式下,我们可以使用ionice命令来调整进程的IO优先级.进程默认分配的IO优先级是根据进程的nice值计算而来的,计算方法可以在man ionice中看到,这里不再废话.

2、deadline:最终期限调度

deadline调度算法相对cfq要简单很多.
其设计目标是:

在保证请求按照设备扇区的顺序进行访问的同时,兼顾其它请求不被饿死,要在一个最终期限前被调度到.

我们知道磁头对磁盘的寻道是可以进行顺序访问和随机访问的,因为寻道延时时间的关系,顺序访问时IO的吞吐量更大,随机访问的吞吐量小.

如果我们想为一个机械硬盘进行吞吐量优化的话,那么就可以让调度器按照尽量复合顺序访问的IO请求进行排序,之后请求以这样的顺序发送给硬盘,就可以使IO的吞吐量更大.

但是这样做也有另一个问题,就是如果此时出现了一个请求,它要访问的磁道离目前磁头所在磁道很远,应用的请求又大量集中在目前磁道附近.

导致大量请求一直会被合并和插队处理,而那个要访问比较远磁道的请求将因为一直不能被调度而饿死.

deadline就是这样一种调度器,能在保证IO最大吞吐量的情况下,尽量使远端请求在一个期限内被调度而不被饿死的调度器.

2.1 deadline设计原理

为了实现上述目标,deadline调度器实现了两类队列,一类负责对请求按照访问扇区进行排序.这个队列使用红黑树组织,叫做sort_list.另一类对请求的访问时间进行排序.使用链表组织,叫做fifo_list.

由于读写请求的明显处理差异,在每一类队列中,又按请求的读写类型分别分了两个队列,就是说deadline调度器实际上有4个队列:

  1. 按照扇区访问顺序排序的读队列;
  2. 按照扇区访问顺序排序的写队列;
  3. 按照请求时间排序的读队列;
  4. 按照请求时间排序的写队列.

deadline之所以要对读写队列进行分离,是因为要实现读操作比写操作更高的优先级.

从应用的角度来看,读操作一般都是同步行为,就是说,读的时候程序一般都要等到数据返回后才能做下一步的处理.

而写操作的同步需求并不明显,一般程序都可以将数据写到缓存,之后由内核负责同步到存储上即可.

所以,对读操作进行优化可以明显的得到收益.当然,deadline在这样的情况下必然要对写操作会饿死的情况进行考虑,保证其不会被饿死.

deadline的入队很简单:当一个新的IO请求产生并进行了必要的合并操作之后,它在deadline调度器中会分别按照扇区顺序和请求产生时间分别入队sort_list和fifo_list.并再进一步根据请求的读写类型入队到相应的读或者写队列.

deadline的出队处理相对麻烦一点:

  1. 首先判断读队列是否为空,如果读队列不为空并且写队列没发生饥饿(starved < writes_starved)则处理读队列,否则处理写队列(第4部).
  2. 进入读队列处理后,首先检查fifo_list中是否有超过最终期限(read_expire)的读请求,如果有则处理该请求以防止被饿死.
  3. 如果上一步为假,则处理顺序的读请求以增大吞吐.
  4. 如果第1部检查读队列为空或者写队列处于饥饿状态,那么应该处理写队列.其过程和读队列处理类似.
  5. 进入写队列处理后,首先检查fifo_list中是否有超过最终期限(write_expire)的写请求,则处理顺序的写请求以增大吞吐.

整个处理逻辑就是这样,简单总结其原则就是,读的优先级高于写,达到deadline时间的请求处理高于顺序处理.正常情况下保证顺序读写,保证吞吐量,有饥饿的情况下处理饥饿.

2.2 deadline的参数调整

deadline的可调参数相对较少,包括:

read_expire:读请求的超时时间设置,单位为ms.当一个读请求入队deadline的时候,其过期时间将被设置为当前时间+read_expire,并放倒fifo_list中进行排序.

write_expire:写请求的超时时间设置,单位为ms.功能根读请求类似.

fifo_batch:在顺序(sort_list)请求进行处理的时候,deadline将以batch为单位进行处理.

每一个batch处理的请求个数为这个参数所限制的个数.在一个batch处理的过程中,不会产生是否超时的检查,也就不会产生额外的磁盘寻道时间.

这个参数可以用来平衡顺序处理和饥饿时间的矛盾,当饥饿时间需要尽可能的符合预期的时候,我们可以调小这个值,以便尽可能多的检查是否有饥饿产生并及时处理.

增大这个值当然也会增大吞吐量,但是会导致处理饥饿请求的延时变长.

writes_starved:这个值是在上述deadline出队处理第一步时做检查用的.用来判断当读队列不为空时,写队列的饥饿程度是否足够高,以时deadline放弃读请求的处理而处理写请求.

当检查存在有写请求的时候,deadline并不会立即对写请求进行处理,而是给相关数据结构中的starved进行累计.

如果这是第一次检查到有写请求进行处理,那么这个计数就为1.如果此时writes_starved值为2,则我们认为此时饥饿程度还不足够高,所以继续处理读请求.

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读