Linux IO调度器

简介:

在现代计算机体系中,机械硬盘仍然作为大部分情况下的存储设备使用,而机械硬盘的访问相对内存差了多个数量级,主要原因在于机械臂转动的寻道时间太长,机械操作没法跟上电子信号的传递,所以OS不可能对每次I/O请求都直接作寻道处理,而是需要额外的工作。

在Linux中,这部分工作主要由I/O调度器完成。

既然时间消耗主要花费在寻道上,那么减少寻道时间就成为I/O调度算法的核心,这主要是通过对I/O请求实施合并和排序完成的。I/O调度器的主要工作是管理像磁盘这种块设备的请求队列,并分配I/O资源给请求。目标是提升全局吞吐量,注意,这意味着I/O请求并不是按照FIFO来处理,而是存在不公平的现象,原理类似于公平锁和非公平锁。

I/O调度器通过合并和排序完成调度任务。

合并是指将两个或多个请求结合成一个新的请求,例如当新来的请求和当前请求队列中的某个请求需要访问的是相同或相邻扇区时,那么就可以把两个请求合并为对同一或多个相邻扇区的请求,这样只需要一次寻道即可。通过合并的做法,多个I/O请求被压缩为一次I/O,最后发送给磁盘的只需要一条寻址命令,就能完成多次寻址同样的效果。显然,合并请求能减少系统开销和磁盘寻址次数。

解决了对相邻扇区的访问,那么对于非相邻扇区呢?我们知道机械臂的转动是朝着扇区增长的方向的,类似于电梯,那么如果我们把I/O请求按照扇区增长排列,一次旋转就可以访问更多的扇区,就可以缩短所有请求的实际寻道时间。这便是I/O调度器的另一项任务:排序。

1、Linus电梯

上面说到排序操作导致的结果和电梯类似,所以早期Linux的I/O调度算法被称为电梯算法,在2.4内核中,被称作Linus电梯。

Linus电梯实现了向前合并和向后合并,对应扇区的增长和减少,而排序操作则如同上面所说的,通过将新请求插入到已有请求队列中合适的位置去实现。那么这里就有一个问题了,如果一个请求比较倒霉,老是被插队怎么办?Linus电梯解决这个问题的办法是当一段时间后检测到队列中有长期没有被处理的请求,那么就暂时中止插入,将新请求直接放到队列尾部,寄希望于顺序处理当前队列中现有的请求来保证响应。但这种做法的缺点在于,I/O调度器并没有真正去处理饥饿的请求,而是采取了一种间接的方式,所以很有可能饥饿的请求仍然饥饿,并没有解决实质的问题。

2、DeadLine

为了解决Linus电梯的饥饿问题,DeadLine在全局吞吐量和延迟方面取了权衡。在DeadLine算法中,每个I/O请求都有一个超时时间,默认读请求是500ms,写请求是5s。

DeadLine除了和Linus电梯一样维护了一个拥有合并和排序功能的请求队列以外,额外维护了两个队列,分别是读请求队列和写请求队列,它们都是带有超时的FIFO队列。当新来一个I/O请求时,会被同时插入普通队列和读/写队列,然后处理普通队列中的请求。当调度器发现读/写请求队列中的请求超时的时候,会优先处理这些请求,保证尽可能不产生请求饥饿。当然,这里会牺牲一定的全局吞吐量。

3、Anticipatory

DeadLine解决了饥饿问题,但是降低了全局吞吐量,当系统大量存在顺序请求时,可能导致请求无法被很好地排序,引发频繁寻道。

Anticipatory是基于预测的I/O算法,大体上它和DeadLine很类似,也维护了三个请求队列。区别在于,当它处理完一个I/O请求以后并不会直接返回处理下一个请求,而是会等待片刻,默认为6ms。如果这时候有新来的针对当前扇区相邻扇区的请求,那么会直接处理它,当等待时间结束后,调度器才返回处理下一个队列请求。

试想一下,如果系统有频繁的针对邻近扇区的I/O请求,那么这种预测算法必然大幅提高整体的吞吐量,毕竟节约了那么多寻道时间。Anticipatory也是当前Linux内核默认的I/O调度器。

更新:在Linux Kernel 2.6.28 的Anticipatory/CFS算法中,对于块设备增加了QUEUE_FLAG_NONROT的标志位,标记随机访问设备以消除等待时间的开销。感谢 @fbcon 的提醒:)

4、CFQ(Complete Fair Queuing)

CFQ把I/O请求按照进程分别放入进程对应的队列中,所以A进程和B进程发出的I/O请求会在两个队列中。而各个队列内部仍然采用合并和排序的方法,区别仅在于,每一个提交I/O请求的进程都有自己的I/O队列。

CFQ的“公平”是针对进程而言的,它以时间片算法为前提,轮转调度队列,默认从当前队列中取4个请求处理,然后处理下一个队列的4个请求。这样就可以确保每个进程享有的I/O资源是均衡的。

5、Noop

Noop做的事情非常简单,它不会对I/O请求排序也不会进行任何其它优化(除了合并)。Noop除了对请求合并以外,不再进行任何处理,直接以类似FIFO的顺序提交I/O请求。

那么为什么我们需要这种调度器呢?因为Noop面向的不是普通的块设备,而是随机访问设备(例如SSD),对于这种设备,不存在传统的寻道时间,那么就没有必要去做那些多余的为了减少寻道时间而采取的事情了。

从上面的算法中我们可以看到,对于不同的场景,选择不同的I/O调度器是十分有必要的。例如对于数据库这种随机访问特别明显的场景,如果使用默认的Anticipatory,就会牺牲大量不必要的等待时间,这时候使用DeadLine通常是更好的选择。对于SSD这种设备,采用Noop或者DeadLine也通常能获得比默认调度器更好的性能。

目录
相关文章
|
11月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
279 0
|
10月前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
363 4
|
10月前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
181 6
|
11月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
482 1
Linux C/C++之IO多路复用(aio)
|
3月前
|
Linux C语言 网络架构
Linux的基础IO内容补充-FILE
而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fwrite函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和fwrite函数打印的数据就有两份。此时我们就可以知道,
66 0
|
3月前
|
存储 Linux Shell
Linux的基础IO
那么,这里我们温习一下操作系统的概念我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。这就是因为在根本上操作系统确实像银行一样,并不完全信任用户程序,因为直接开放底层资源(如内存、磁盘、硬件访问权限)给用户程序会带来巨大的风险。所以就向银行一样他的服务是由工作人员隔着一层玻璃,然后对顾客进行服务的。
50 0
|
9月前
|
Ubuntu Linux Shell
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
(已成功解决)Linux环境报错—bash: wget: command not found;常见Linux发行版本,Linux中yum、rpm、apt-get、wget的区别;Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
4403 69
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
|
7月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
303 34
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
9月前
|
Linux API C语言
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
203 14