FIFO (Read) > FIFO (Write) > CFQ 从编程角度而已,一般磁盘I/O的场景有以下四种:
(1)用户调用标准C库进行I/O操作,数据流为:应用程序buffer->C库标准IObuffer->文件系统页缓存->通过具体文件系统到磁盘
(2)用户调用文件I/O,数据流问:应用程序buffer->文件系统页缓存->通过具体文件系统到磁盘
(3)用户打开文件时使用O_DIRECT,绕开页缓存直接读写磁盘
(4)用户使用类似dd工具,并使用direct参数,绕过系统cache与文件系统直接写磁盘。
发起I/O请求的步骤可以表述为如下的内容(以最长链路为例)
写操作:用户调用fwrite把数据写入C库标准IObuffer后就返回,即写操作通常是异步操作;
数据写入C库标准IObuffer后,不会立即刷新到磁盘,会将多次小数据量相邻写操作先缓存起来合并,最终调用write函数一次写入(或者将大块数据分解多次write调用)页缓存;数据到达页缓存后也不会立即刷新到磁盘,内核有pdflush线程在不停地检测脏页,判断是否要写回到磁盘,如果是则发起磁盘I/O请求。
读操作:用户调用fread到C库标准IObuffer中读取数据,如果成功则返回,否则继续;发起I/O请求,读取数据后缓存buffer和C库标准IObuffer并返回。可以看出,读操作是同步请求。
I/O请求处理:通用块层根据I/O请求构造一个或多个bio结构并提交给调度层;调度器将bio结构进行排序和合并组织成队列且确保写操作合并到一起写,尽可能变随机为顺序(因为随机读写比顺序读写要慢),读必须优先满足,而写也不能等太久。
针对不同的应用场景,I/O调度处理也会影响I/O的读写性能,目前linux系统中的I/O调度策略有4种,分布是NOOP、CFQ、DEADLINE和ANTICIPATORY,默认为CFQ。
1.NOOP
NOOP算法的全写为No Operation.该算法实现了最简单的FIFO队列,所有I/O请求大致按照先来后到的顺序进行操作。之所以说“大致”,原因是NOOP在FIFO的基础上还做了相邻I/O请求的合并,并不是完全按照现金先出的规则满足I/O请求。
假设有如下的I/O请求序列:
100,500,101,10,56,1000
NOOP将会按照如下顺序满足I/O请求:
100(101),500,10,56,1000
2.CFQ
CFQ算法的全写为Completely Fair Queuing。该算法的特定是按照I/O请求的地址进行排序,而不是按照先来后到的顺序进行响应。
假设有如下的I/O请求序列:
100,500,101,10,56,1000
CFQ将会按照如下顺序满足:
100, 101,500,1000,10,56
CFQ是默认的磁盘调度算法,对于通用服务器来说是最好的选择。它试图均匀地分布对I/O带宽的访问。CFQ为每个进程单独创建一个队列来管理该进程所产生的请求,也就是说,每个进程一个队列,各队列之间的调度使用时间片段进行调度,以此来保证每个进程都能被很好第分配到I/O带宽。I/O调度器每次执行一个进程的4次请求。在传统的SAS盘上,磁盘寻道花去了绝大多数的I/O响应时间。CFQ的出发点是对I/O地址进行排序,以尽量少的磁盘旋转次数来满足尽可能多的I/O请求。在CFQ算法下,SAS盘的吞吐量大大提高了。相比于NOOP的缺点是,先来的I/O请求并不一定能被满足,可能会出现“饿死”的情况。
3.DEADLINE