Linux内核2。6开始引入了全新的IO调度子系统,IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调度器也被叫做电梯。 (elevator)而相应的算法也就被叫做电梯算法。而Linux中IO调度的电梯算法有好如下几种:as(Anticipatory)、cfq(Complete Fairness Queueing)、deadline、noop(No Operation)。具体使用哪种算法我们可以在启动的时候通过内核参数elevator来指定,默认使用的算法是cfq。

一、NOOP算法

NOOP算法的全写为No Operation。该算法实现了最最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作。之所以说“大致”,原因是NOOP在FIFO的基础上还做了相邻IO请求的合并,并不是完完全全按照先进先出的规则满足IO请求。

假设有如下的io请求序列:

1100,500,101,10,56,1000

NOOP将会按照如下顺序满足:

1100(101),500,10,56,1000

NOOP是在Linux2。4或更早的版本的使用的唯一调度算法。由于该算法比较简单,减了IO占用CPU中的计算时间最少。不过容易造成IO请求饿死。关于IO饿死的描述如下:因为写请求比读请求更容易。写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中。读请求需要等到它前面所有的读操作完成,才能进行下一次读操作。在读操作之间有几毫秒时间,而写请求在这之间就到来 ,饿死了后面的读请求 。其适用于SSD或Fusion IO环境下。

二、CFQ算法

该算法的特点是按照IO请求的地址进行排序,而不是按照先来后到的顺序来进行响应。CFQ为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度, 以此来保证每个进程都能被很好的分配到I/O带宽。假设有如下的io请求序列:

1100,500,101,10,56,1000

CFQ将会按照如下顺序满足:

1100,101,500,1000,10,56

在传统的SAS盘上,磁盘寻道花去了绝大多数的IO响应时间。CFQ的出发点是对IO地址进行排序,以尽量少的磁盘旋转次数来满足尽可能多的IO请求。在CFQ算法下,SAS盘的吞吐量大大提高了。但是相比于NOOP的缺点是,先来的IO请求并不一定能被满足,也可能会出现饿死的情况,不过其作为最新的内核版本和发行版中默认的I/O调度器,对于通用的服务器也是最好的选择。CFQ是deadline和as调度器的折中,CFQ对于多媒体应用(video,audio)和桌面系统是最好的选择。

三、DEADLINE算法

DEADLINE在CFQ的基础上,解决了IO请求饿死的极端情况。除了CFQ本身具有的IO排序队列之外,DEADLINE额外分别为读IO和写IO提供了FIFO队列。读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5s。FIFO队列内的IO请求优先级要比CFQ队列中的高,,而读FIFO队列的优先级又比写FIFO队列的优先级高。优先级可以表示如下:

1FIFO(Read) > FIFO(Write) > CFQ

Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限。这样就防止了写操作因为不能被读取而饿死的现象。Deadline对数据库环境(ORACLE RAC,MYSQL等)是最好的选择。

四、ANTICIPATORY算法

CFQ和DEADLINE考虑的焦点在于满足零散IO请求上。对于连续的IO请求,比如顺序读,并没有做优化。为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。ANTICIPATORY的在DEADLINE的基础上,为每个读IO都设置了6ms的等待时间窗口。如果在这6ms内OS收到了相邻位置的读IO请求,就可以立即满足。

本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度。可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价。它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量。AS适合于写入较多的环境,比如文件服务器,但对对数据库环境表现很差。

五、IO调度配置

1、查看当前系统支持的IO调度算法

1# dmesg | grep -i scheduler
2io scheduler noop registered
3io scheduler anticipatory registered
4io scheduler deadline registered
5io scheduler cfq registered (default)

2、查看当前系统的I/O调度方法

1# cat /sys/block/sda/queue/scheduler
2noop anticipatory deadline [cfq]

3、临地更改I/O调度方法

1# echo noop > /sys/block/sda/queue/scheduler

4、永久的更改I/O调度方法

修改内核引导参数,加入elevator=调度程序名

1# vim /boot/grub/menu。lst
2更改到如下内容:
3kernel /boot/vmlinuz-2。6。32-504。el6 ro root=LABEL=/ elevator=deadline rhgb quiet

5、相关参数

除NOOP算法由于比较简单无相关参数可调外,其他三种算法都有相关参数可以调节。

 1#cfq算法下的相关参数
 2[root@361way queue]# echo "cfq" > /sys/block/sda/queue/scheduler
 3[root@361way iosched]# ls -l /sys/block/sda/queue/iosched
 4total 0
 5-rw-r--r-- 1 root root 4096 Oct 10 12:40 back_seek_max
 6-rw-r--r-- 1 root root 4096 Oct 10 12:40 back_seek_penalty
 7-rw-r--r-- 1 root root 4096 Oct 10 12:40 fifo_expire_async
 8-rw-r--r-- 1 root root 4096 Oct 10 12:40 fifo_expire_sync
 9-rw-r--r-- 1 root root 4096 Oct 10 12:40 group_idle
10-rw-r--r-- 1 root root 4096 Oct 10 12:40 group_isolation
11-rw-r--r-- 1 root root 4096 Oct 10 12:40 low_latency
12-rw-r--r-- 1 root root 4096 Oct 10 12:03 quantum
13-rw-r--r-- 1 root root 4096 Oct 10 12:40 slice_async
14-rw-r--r-- 1 root root 4096 Oct 10 12:40 slice_async_rq
15-rw-r--r-- 1 root root 4096 Oct 10 12:03 slice_idle
16-rw-r--r-- 1 root root 4096 Oct 10 12:40 slice_sync
17#deadline算法相关参数
18[root@361way queue]# echo "deadline" > /sys/block/sda/queue/scheduler
19[root@361way iosched]# ls -l /sys/block/sda/queue/iosched
20total 0
21-rw-r--r-- 1 root root 4096 Oct 10 12:42 fifo_batch
22-rw-r--r-- 1 root root 4096 Oct 10 12:42 front_merges
23-rw-r--r-- 1 root root 4096 Oct 10 12:42 read_expire
24-rw-r--r-- 1 root root 4096 Oct 10 12:42 write_expire
25-rw-r--r-- 1 root root 4096 Oct 10 12:42 writes_starved
26#anticipatory相关参数
27[root@361way queue]# echo "anticipatory" > /sys/block/sda/queue/scheduler
28[root@361way iosched]# ls -l /sys/block/sda/queue/iosched
29total 0
30-rw-r--r-- 1 root root 4096 Oct 10 12:44 antic_expire
31-r--r--r-- 1 root root 4096 Oct 10 12:44 est_time
32-rw-r--r-- 1 root root 4096 Oct 10 12:44 read_batch_expire
33-rw-r--r-- 1 root root 4096 Oct 10 12:44 read_expire
34-rw-r--r-- 1 root root 4096 Oct 10 12:44 write_batch_expire
35-rw-r--r-- 1 root root 4096 Oct 10 12:44 write_expire

模式和相关参数调整后可以简单的使用dd进行IO读写测试,也可以使用专业的工具fio进行测试 。并根据自己的业务模型确认最优方案。

1读测试
2# time dd if=/dev/sda1 of=/dev/null bs=2M count=300
3写测试
4# time dd if=/dev/zero of=/tmp/test bs=2M count=300

六、ionice

ionice可以更改任务的类型和优先级,不过只有cfq调度程序可以用ionice。

1采用cfq的实时调度,优先级为7
2ionice -c1 -n7 -ptime dd if=/dev/sda1 f=/tmp/test bs=2M count=300&
3采用缺省的磁盘I/O调度,优先级为3
4ionice -c2 -n3 -ptime dd if=/dev/sda1 f=/tmp/test bs=2M count=300&
5采用空闲的磁盘调度,优先级为0
6ionice -c3 -n0 -ptime dd if=/dev/sda1 f=/tmp/test bs=2M count=300&

ionice的磁盘调度优先级有8种,最高是0,最低是7。注意,磁盘调度的优先级与进程nice的优先级没有关系。一个是针对进程I/O的优先级,一个是针对进程CPU的优先级。

七、总结

CFQ和DEADLINE考虑的焦点在于满足零散IO请求上。对于连续的IO请求,比如顺序读,并没有做优化。为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。ANTICIPATORY的在DEADLINE的基础上,为每个读IO都设置了6ms的等待时间窗口。如果在这6ms内OS收到了相邻位置的读IO请求,就可以立即满足。

IO调度器算法的选择,既取决于硬件特征,也取决于应用场景。 在传统的SAS盘上,CFQ、DEADLINE、ANTICIPATORY都是不错的选择;对于专属的数据库服务器,DEADLINE的吞吐量和响应时间都表现良好。然而在新兴的固态硬盘比如SSD、Fusion IO上,最简单的NOOP反而可能是最好的算法,因为其他三个算法的优化是基于缩短寻道时间的,而固态硬盘没有所谓的寻道时间且IO响应时间非常短。

参考页面:

torvalds github页面
IOSched wiki