Linux I/O 调度方法

   Linux IO调度程序是块设备I/O子系统的主要组件,它介于通用块层和块设备驱动程序之间。当Linux内核组件要读写一些数据时,并不是请求一发出,内核便立即执行该请求,而是将其推迟执行。延迟的设定是块设备性能的关键机制!当传输一个新数据块时,内核检查能否通过扩展前一个一直处于等待状态的请求而满足新请求。


   IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调度器也被叫做电梯. (elevator)而相应的算法也就被叫做电梯算法.而Linux中IO调度的电梯算法有如下4种:分别为预期(Anticipatory)算法、最后期限(Deadline)算法、完全公平对列(CFQ)算法以及NOOP算法(No Operation)。用户可在内核引导时指定一种I/O调度算法(例如Centos,只需在grub.conf文件中加入类似如下行 elevator=deadline),也可在运行时通过 sysfs 文件系统/sys/block/sda/queue/scheduler改变块设备的I/O调度算法(cat可查看当前使用IO调度算法)。默认的IO调度程序是CFQ.

一)I/O调度的4种算法

1)CFQ(完全公平排队I/O调度程序)

特点:
在最新的内核版本和发行版中,都选择CFQ做为默认的I/O调度器,对于通用的服务器也是最好的选择.CFQ试图均匀地分布对I/O带宽的访问,避免进程被饿死并实现较低的延迟,是deadline和as调度器的折中.CFQ赋予I/O请求一个优先级,而I/O优先级请求独立于进程优先级,高优先级的进程的读写不能自动地继承高的I/O优先级.CFQ调度算法适用于系统中存在多任务I/O请求的情况,通过在多进程中轮换,保证了系统I/O请求整体的低延迟。但是,对于只有少数进程存在大量密集的I/O请求的情况,则会出现明显的I/O性能下降。

工作原理:
CFQ为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度,以此来保证每个进程都能被很好的分配到I/O带宽.I/O调度器每次执行一个进程的4次请求.

调优:
CFQ调度器主要提供如下三个优化参数:
A)slice_idle
如果一个进程在自己的时间窗口里,经过slice_idle时间都没有发射I/O请求,则调度选择下一个程序。通过该机制,可以有效利用I/O请求的局部性原理,提高系统的I/O吞吐量。
B)quantum
该参数控制在一个时间窗口内可以发射的I/O请求的最大数目。
C)low_latency
对于I/O请求延时非常重要的任务,将该参数设置为1可以降低I/O请求的延时。

2)NOOP(电梯式调度程序)

特点:
NOOP实现了一个简单的FIFO队列,它像电梯的工作主法一样对I/O请求进行组织,当有一个新的请求到来时,它将请求合并到最近的请求之后,以此来保证请求同一介质.
NOOP倾向饿死读而利于写.电梯算法饿死读请求的解释:因为写请求比读请求更容易.写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中.读请求需要等到它前面所有的读操作完成,才能进行下一次读操作.在读操作之间有几毫秒时间,而写请求在这之间就到来,饿死了后面的读请求.

3)Deadline(截止时间调度程序)

特点:
通过时间以及硬盘区域进行分类,这个分类和合并要求类似于noop的调度程序.Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限.这样就防止了写操作因为不能被读取而饿死的现象.

工作原理:
DEADLINE调度算法主要针对I/O请求的延时而设计,每个I/O请求都被附加一个最后执行期限。该算法维护两类队列,一是按照扇区排序的读写请求队列;二是按照过期时间排序的读写请求队列。如果当前没有I/O请求过期,则会按照扇区顺序执行I/O请求;如果发现过期的I/O请求,则会处理按照过期时间排序的队列,直到所有过期请求都被发射为止。在处理请求时,该算法会优先考虑读请求.

调优:
DEADLINE调度算法提供如下三个参数:
A)writes_starved
该参数控制当读写队列均不为空时,发射多少个读请求后,允许发射写请求。
B)read_expire
参数控制读请求的过期时间,单位毫秒。
C)write_expire
参数控制写请求的过期时间,单位毫秒。

4)AS(预期I/O调度程序)

特点:
本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度.可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价.它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量.

工作原理:
算法统计系统中每个进程触发的I/O操作的种类。当刚刚调度了由某个进程p发出的一个读请求之后,算法马上检查排序队列中的下一个请求是否来自同一个进程p。如果是,立即调度下一个请求。否则,查看关于该进程p的统计信息:如果确定进程p可能很快发出另一个读请求,那么就延迟一小段时间(缺省大约为7ms)。因此,算法预测进程p发出的读请求与刚被调度的请求在磁盘上可能是“近邻”。

调优:
antic_expire ,read_expire 和 write_expire.
根据上面的内容,我们算法中可能用的最多的就是 Anticipatory 的算法了,会根据时间来多排一些内容在写,所以下面讲讲这个参数可以调的部分.除了算法修改成这个算法外,影响它的还有
磁盘队列长度
/sys/block/sda/queue/nr_requests 默认只有 128 个队列,可以提高到 512 个.会更加占用内存,但能更加多的合并读写操作,速度变慢,但能读写更加多的量
等待时间
/sys/block/sda/queue/iosched/antic_expire 读取附近产生的新请时等待多长时间
对读优化的参数
/sys/block/sda/queue/read_ahead_kb
这个参数对顺序读非常有用,意思是,一次提前读多少内容,无论实际需要多少.默认一次读 128kb 远小于要读的,设置大些对读大文件非常有用,可以有效的减少读 seek 的次数,这个参数可以使用 blockdev –setra 来设置,setra 设置的是多少个扇区,所以实际的字节是除以2,比如设置 512 ,实际是读 256 个字节.

常用命令

1.dmesg | grep -i scheduler //查看当前系统支持的IO调度算法
2.cat /sys/block/sda/queue/scheduler //查看当前系统的IO调度算法
3.echo noop > /sys/block/sda/queue/scheduler //临地更改I/O调度方法:

二)ionice

ionice可以更改任务的类型和优先级,不过只有cfq调度程序可以用ionice.
有三个例子说明ionice的功能:
采用cfq的实时调度,优先级为7
ionice -c1 -n7 -ptime dd if=/dev/sda1 f=/tmp/test bs=2M count=300&
采用缺省的磁盘I/O调度,优先级为3
ionice -c2 -n3 -ptime dd if=/dev/sda1 f=/tmp/test bs=2M count=300&
采用空闲的磁盘调度,优先级为0
ionice -c3 -n0 -ptime dd if=/dev/sda1 f=/tmp/test bs=2M count=300&
ionice的三种调度方法,实时调度最高,其次是缺省的I/O调度,最后是空闲的磁盘调度.
ionice的磁盘调度优先级有8种,最高是0,最低是7.
注意,磁盘调度的优先级与进程nice的优先级没有关系.
一个是针对进程I/O的优先级,一个是针对进程CPU的优先级.

几个非常有效的 IO 调度调节的内核参数

/proc/sys/vm/dirty_ratio
这个参数控制文件系统的文件系统写缓冲区的大小,单位是百分比,表示系统内存的百分比,表示当写缓冲使用到系统内存多少的时候,开始向磁盘写出数 据.增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能.但是,当你需要持续、恒定的写入场合时,应该降低其数值,一般启动上缺省是 10.下面是增大的方法: echo ’40′> /proc/sys/vm/dirty_ratio

/proc/sys/vm/dirty_background_ratio
这个参数控制文件系统的pdflush进程,在何时刷新磁盘.单位是百分比,表示系统内存的百分比,意思是当写缓冲使用到系统内存多少的时候, pdflush开始向磁盘写出数据.增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能.但是,当你需要持续、恒定的写入场合时,应该降低其数值,一般启动上缺省是 5.下面是增大的方法: echo ’20′ > /proc/sys/vm/dirty_background_ratio

/proc/sys/vm/dirty_writeback_centisecs
这个参数控制内核的脏数据刷新进程pdflush的运行间隔.单位是 1/100 秒.缺省数值是500,也就是 5 秒.如果你的系统是持续地写入动作,那么实际上还是降低这个数值比较好,这样可以把尖峰的写操作削平成多次写操作.设置方法如下: echo ’200′ > /proc/sys/vm/dirty_writeback_centisecs 如果你的系统是短期地尖峰式的写操作,并且写入数据不大(几十M/次)且内存有比较多富裕,那么应该增大此数值: echo ’1000′ > /proc/sys/vm/dirty_writeback_centisecs

/proc/sys/vm/dirty_expire_centisecs
这个参数声明Linux内核写缓冲区里面的数据多“旧”了之后,pdflush进程就开始考虑写到磁盘中去.单位是 1/100秒.缺省是 30000,也就是 30 秒的数据就算旧了,将会刷新磁盘.对于特别重载的写操作来说,这个值适当缩小也是好的,但也不能缩小太多,因为缩小太多也会导致IO提高太快.建议设置为 1500,也就是15秒算旧. echo ’1500′ > /proc/sys/vm/dirty_expire_centisecs 当然,如果你的系统内存比较大,并且写入模式是间歇式的,并且每次写入的数据不大(比如几十M),那么这个值还是大些的好.

应用场景

Anticipatory I/O scheduler 适用于大多数环境,特别是写入较多的环境(比如文件服务器)Web,App等应用,但不太合适数据库应用,
Deadline I/O scheduler 通常与Anticipatory相当,但更简洁小巧,更适合于数据库应用
CFQ I/O scheduler 为所有进程分配等量的带宽,适合于桌面多任务及多媒体应用,适用于有大量进程的多用户系统,默认IO调度器
NOOP I/O scheduler NOOP对于闪存设备,RAM,嵌入式系统是最好的选择.其应用环境主要有以下两种:一是物理设备包含自己的I/O调度程序,比如SCSI的TCQ;二是寻道时间可以忽略不计的设备,比如SSD等。

最后:Linux内核允许用户为每个单独的块I/O设备设置不同的I/O调度算法。这样,根据块设备的不同以及读写该设备的应用不同,可以最大限度的提升系统的I/O吞吐率。通过在/etc/fstab 文件中修改相应的调度算法。

你可能感兴趣的:(linux)