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请求序列:

100,500,101,10,56,1000

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

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

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

二、CFQ算法

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

100,500,101,10,56,1000

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

100,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队列的优先级高。优先级可以表示如下:

FIFO(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调度算法

root@(none):~# cat /sys/block/mmcblk0/queue/scheduler
noop deadline [cfq]
root@(none):~#
root@czl-VirtualBox:/# cat /sys/block/sdb/queue/scheduler 
[mq-deadline] none
root@czl-VirtualBox:/# cat /sys/block/sda/queue/scheduler
[mq-deadline] none
root@czl-VirtualBox:/#

修改调度策略

root@czl-VirtualBox:/# echo none > /sys/block/sda/queue/scheduler
root@czl-VirtualBox:/# cat /sys/block/sda/queue/scheduler
[none] mq-deadline
root@czl-VirtualBox:/#

永久修改调度策略

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

六,测试方式:

Linux上可以简单的使用 time命令以及dd命令对磁盘的读写性能进行测试.

root@czl-VirtualBox:/#  time dd if=/dev/sdb of=/dev/null bs=2M count=300
300+0 records in
300+0 records out
629145600 bytes (629 MB, 600 MiB) copied, 186.119 s, 3.4 MB/s

real 3m6.146s
user 0m0.000s
sys 0m0.475s
root@czl-VirtualBox:/#

七,其它调试手段

/proc/sys挂载的文件系统是:

/proc/sys
root@czl-VirtualBox:/proc# tree sys -d  -L 2
sys
├── abi
├── debug
├── dev
│ ├── cdrom
│ ├── hpet
│ ├── mac_hid
│ ├── parport
│ ├── raid
│ ├── scsi
│ └── tty
├── fs
│ ├── binfmt_misc
│ ├── epoll
│ ├── inotify
│ ├── mqueue
│ ├── quota
│ └── verity
├── kernel
│ ├── firmware_config
│ ├── keys
│ ├── pty
│ ├── random
│ ├── sched_domain
│ ├── seccomp
│ ├── usermodehelper
│ └── yama
├── net
│ ├── core
│ ├── ipv4
│ ├── ipv6
│ ├── netfilter
│ └── unix
├── user
└── vm

34 directories
root@czl-VirtualBox:/proc#

对应的内核代码位于:

Linux IO调度(电梯算法)以及常用调试方式_优先级

 

Linux IO调度(电梯算法)以及常用调试方式_调度算法_02

block_dump对应于文件系统中的/proc/sys/vm/block_dump节点:

Linux IO调度(电梯算法)以及常用调试方式_等待时间_03

 当它被使能的时候(通过echo 1 > block_dump)打开

Linux IO调度(电梯算法)以及常用调试方式_等待时间_04

Linux IO调度(电梯算法)以及常用调试方式_等待时间_05

Linux IO调度(电梯算法)以及常用调试方式_调度算法_06

验证:

Linux IO调度(电梯算法)以及常用调试方式_调度算法_07

为什么是submit_bio?

因为,submit_bio几乎是所有块请求的统一入口,在这里可以拦截到来自于各个应用的块儿请求数据。

Linux IO调度(电梯算法)以及常用调试方式_优先级_08

Linux IO调度(电梯算法)以及常用调试方式_优先级_09


总结:

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响应时间非常短。


结束!