Linux 的文件 IO 子系统是 Linux 中最复杂的一个子系统(没有之一)。

目前Linux系统中的I/O调度策略有4种,分别为(默认为CFQ):
- NOOP
- CFQ(默认)
- DeadLine
- Anticipatory(被删除)
NOOP
NOOP 全称 No Operation,该算法实现了最简单的 FIFO 队列,所有 IO 请求大致按照先来后到的顺序进行操作。之所以说“大致”,原因是 NOOP 在 FIFO 的基础上还做了相邻 IO 请求的合并,并不是完全按照先进先出的规则满足I/O请求。
假设有如下的 IO 请求序列:
1 | 100 500 101 10 56 1000 |
NOOP 将会按照如下顺序满足 IO 请求:
1 | 100(101) 500 10 56 1000 |
CFQ
CFQ 全称 Completely Fair Queuing,该算法的特点是按照I/O请求的地址进行排序进行响应。
CFQ 是默认的磁盘调度算法,它试图均匀地分布对 IO 带宽的访问,对于通用服务器来说是最好的选择。
CFQ 为每个进程单独创建一个队列来管理该进程所产生的请求。每个进程一个队列,各队列之间的调度使用时间片进行调度,以此来保证每个进程都能被很好地分配到 IO 带宽。IO 调度器每次执行一个进程的4次请求。在传统的SAS盘上,磁盘寻道花去了绝大多数的 IO 响应时间。
CFQ 的出发点是对 IO 地址进行排序,以尽量少的磁盘旋转次数来满足尽可能多的 IO 请求。在 CFQ 算法下,SAS盘的吞吐量大大提高了。相比于 NOOP 的缺点是,先来的 IO 请求并不一定能被满足,可能会出现“饿死”的情况。(这里应该说:CFQ试图均匀地分布对I/O带宽的访问,避免进程被饿死并实现较低的延迟,是deadline和as调度器的折中)
假设有如下的 IO 请求序列:
1 | 100 500 101 10 56 1000 |
CFQ 将会按照如下顺序满足:(注意下面只有前4个IO请求进行了排序)
1 | 100 101 500 1000 10 56 |
DeadLine
DeadLine 在 CFQ 的基础上,解决了 IO 请求“饿死”的极端情况。
除了 CFQ 本身具有的 IO 排序队列,DeadLine 额外分别为读 IO 和写 IO 提供了FIFO队列。读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5s。FIFO队列内的 IO 请求优先级要比 CFQ 队列中的高,而读FIFO队列的优先级又比写FIFO队列的优先级高。优先级可以表示如下:
1 | FIFO(read) > FIFO(write) > CFQ |
Anticipatory
Anticipatory 在 DeadLine 的基础上,为每个读I/O都设置了6ms的等待时间窗口。如果在6ms内收到了相邻位置的读I/O请求,就可以立即满足。
Anticipatory 算法通过增加等待时间来获得更高的性能,假设一个块设备只有一个物理查找磁头(例如一个单独的SATA硬盘),将多个随机的小写入流合并成一个大写入流(相当于将随机读写变顺序读写),通过这个原理来使用读取/写入的延时换取最大的读取/写入吞吐量。
Anticipatory 算法从Linux 2.6.33版本后被删除了,因为使用 CFQ 通过配置也能达到 Anticipatory 的效果。
小结
Linux I/O调度器是 Linux 内核中的一个组成部分,用户可以通过根据不同的存储器来设置 Linux I/O 调度器从而达到优化系统性能。通常来说:
- NOOP 调度器适用于固态硬盘
- CFQ 调度器适用于桌面多任务及媒体应用
- DeadLine 调度器适用于写入较多的文件服务器,比如Web服务器,数据库应用
相关命令
1 | 查看磁盘的IO调度策略,注意:其中sda是硬盘名 |