概述
在介绍IO调度之前,首先需要了解一些基础概念。
IO设备
I/O设备即输入/输出设备,是指能够与计算机进行数据的输入和输出交流的硬件设备,常见的如鼠标、键盘、扫描仪、数码相机属于输入设备,显示器、打印机、刻录机属于输出设备。I/O设备是与电脑密切相关的外设设备的总称。根据性质不同,IO设备可以分为不同的类别:
IO设备的分类
1.按传输速率分类
低速设备:指传输速率为每秒钟几个字节到数百个字节的设备。典型的设备有键盘、鼠标、语音的输入等
中速设备:指传输速率在每秒钟数千个字节至数十千个字节的设备。典型的设备有行式打印机、激光打印机等
高速设备:指传输速率在数百千个字节至数兆字节的设备。典型的设备有磁带机、磁盘机、光盘机等
2.按信息交换的单位分类
块设备(Block Device):指以数据块为单位来组织和传送数据信息的设备。这类设备用于存储信息,有磁盘和磁带等。 它属于有结构设备。典型的块设备是磁盘,每个盘块的大小 为512B~4KB,磁盘设备的基本特征是:①传输速率较高,通常每秒钟为几兆位;②它是可寻址的,即可随机地读/写任意一块;③磁盘设备的I/O采用DMA方式。
DMA(直接内存访问,Direct Memory Access)取代了PIO(读取磁盘文件到内存中,数据要通过CPU存储转发),它能够不通过CPU而直接进行磁盘和内存的数据交换。在DMA模式下,CPU只须要向DMA控制器下达指令,让DMA控制器来处理数据的传送便可,DMA控制器经过系统总线来传输数据,传送完毕再通知CPU,这样就在很大程度上下降了CPU占有率,大大节省了系统资源
字符设备(Character Device):指以单个字符为单位来送数据信息的设备。这类设备一般用于数据的输入和输出,有交互式终端、打印机等。字符设备的基本特征是:①传输速率较低;②不可寻址,即不能指定输入时的源地址或输出时的目标地址;③字符设备的I/O常采用中断驱动方式。
3.按资源分配的角度分类
独占设备:指在一段时间内只允许一个用户(进程)访问的独占设备。大多数低速的I/O设备如用户终端、打印机等属于这类设备。因为独占设备属于临界资源,所以多个并发进程必须互斥地进行访问。
共享设备:指在一段时间内允许多个进程同时访问的设备。共享设备必须是可寻址的和可随机访问的设备。典型的共享设备是磁盘。共享设备不仅可以获得良好的设备利用率,而且是实现文件系统和数据库系统的物质基础。
虚拟设备:指通过虚拟技术将一台独占设备变换为若干台供多个用户(进程)共享的逻辑设备。
块设备
前面已经介绍过了块设备,块设备的基本特征是将数据存储到固定大小的块中,每个块都是可寻址的,而且互相独立于其他块可进行读写。这里以机械硬盘为例,分析其结构与寻址过程,只有理解了这个过程才能更加深入理解IO调度的目的:减少磁头的移动,提高效率。下面的两个图片展示了磁盘的物理结构:
存储容量 = 磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数
如上图中的磁盘有3个圆盘、6个磁头、7个柱面(每个盘片7个磁道),每条磁道有12个扇区,所以此磁盘的容量为
6 * 7 * 12 * 512B = 258048B
每个磁道的扇区数一样是说的老的硬盘,外圈的密度小,内圈的密度大,每圈可存储的数据量是一样的,比如512B。新的硬盘数据的密度都一致,这样磁道的周长越长,扇区就越多,存储的数据量就越大。
磁盘读取时间 = 寻道时间 + 旋转时间 + 传输时间
寻道时间:磁头从开始移动到数据所在磁道所需要的时间,寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3-15ms,一般都在10ms左右。
旋转延迟:盘片旋转将请求数据所在扇区移至读写磁头下方所需要的时间, 旋转延迟取决于磁盘转速。普通硬盘一般都是7200rpm,慢的5400rpm。
数据传输时间:完成传输所请求的数据所需要的时间。
由于传输时间要视磁盘的性质,所以要提高磁盘的IO效率能够优化的就是寻道时间和旋转延迟。我们可以通过后文介绍的IO调度器来实现这一点。
IO体系
Linux的IO体系如下图所示:
如图整个IO体系可以分为七层:
- VFS虚拟文件系统:内核要跟多种文件系统打交道,内核抽象了这VFS,专门用来适配各种文件系统,并对外提供统一操作接口。
- 磁盘缓存:磁盘缓存是一种将磁盘上的一些数据保留到RAM中的软件机制,这使得对这部分数据的访问可以得到更快的响应。磁盘缓存在Linux中有三种类型:Dentry cache ,Page cache , Buffer cache。
- 映射层:内核从块设备上读取数据,这样内核就必须确定数据在物理设备上的位置,这由映射层(Mapping Layer)来完成。
- 通用块层:由于绝大多数情况的I/O操作是跟块设备打交道,所以Linux在此提供了一个类似vfs层的块设备操作抽象层。下层对接各种不同属性的块设备,对上提供统一的Block IO请求标准。
- I/O调度层:大多数的块设备都是磁盘设备,所以有必要根据这类设备的特点以及应用特点来设置一些不同的调度器。
- 块设备驱动:块设备驱动对外提供高级的设备操作接口。
- 物理硬盘:这层就是具体的物理设备。
假设一个进程使用系统调用read()读取磁盘上的文件。下面步骤是内核响应进程读请求的步骤:
- 系统调用read()会触发相应的VFS(Virtual Filesystem Switch)函数,传递的参数有文件描述符和文件偏移量。
- VFS确定请求的数据是否已经在内存缓冲区中;若数据不在内存中,确定如何执行读操作。
- 假设内核必须从块设备上读取数据,这样内核就必须确定数据在物理设备上的位置。这由映射层(Mapping Layer)来完成。
- 此时内核通过通用块设备层(Generic Block Layer)在块设备上执行读操作,启动I/O操作,传输请求的数据。
- 在通用块设备层之下是I/O调度层(I/O Scheduler Layer),根据内核的调度策略,对等待的I/O等待队列合并和排序。
- 最后,块设备驱动(Block Device Driver)通过向磁盘控制器发送相应的命令,执行真正的数据传输。
有了上文的铺垫,我们能够清楚IO调度在整个IO体系中所处的位置,它是提高系统IO效率的一种手段,下文将对其进行介绍。
IO调度器简介
IO调度器(IO Scheduler)是操作系统用来决定块设备上IO操作提交顺序的方法。存在的目的有两个,一是提高IO吞吐量,二是降低IO响应时间。然而IO吞吐量和IO响应时间往往是矛盾的,为了尽量平衡这两者,IO调度器提供了多种调度算法来适应不同的IO请求场景。
Linux 从2.4内核开始支持I/O调度器,到目前为止有5种类型:Linux 2.4内核的 Linus Elevator、Linux 2.6内核的 Deadline、 Anticipatory、 CFQ、 Noop,其中Anticipatory从Linux 2.6.33版本后被删除了。目前主流的Linux发行版本使用Deadline、 CFQ、 Noop三种I/O调度器,下面将对这三种调度器进行介绍
NOOP
Noop即No Operation,它不会对I/O请求排序也不会进行任何其它优化(除了合并)。Noop除了对请求合并以外,不再进行任何处理,直接以类似FIFO的顺序提交I/O请求
DeadLine
DeadLine 实现了四个队列,其中两个分别处理正常的 read 和 write,按扇区号排序,进行正常 IO 的合并处理以提高吞吐量。 因为 IO 请求可能会集中在某些磁盘位置,这样会导致新来的请求一直被合并,可能会有其他磁盘位置的 IO 请求被饿死。 因此实现了另外两个处理超时 read 和 write 的队列,调度算法保证超时(达到最终期限时间,默认读为500ms,写为5s)的队列中的请求会优先被处理,防止请求被饿死
CFQ
CFQ(Completely Fair Queuing, 完全公平排队)算法把I/O请求分别放入进程对应的队列中,所以A进程和B进程发出的I/O请求会在两个队列中。而各个队列内部仍然采用合并和排序的方法,区别仅在于,每一个提交I/O请求的进程都有自己的I/O队列。
CFQ的“公平”是针对进程而言的,它以时间片算法为前提,轮转调度队列,默认从当前队列中取4个请求处理,然后处理下一个队列的4个请求。这样就可以确保每个进程享有的I/O资源是均衡的。
从Linux 2.6.18起,CFQ作为系统默认的IO调度算法
不同调度算法的应用场景
Noop算法由于其简单的特性,在以下场景更适合:
- 在IO调度器下方有更加智能的IO调度设备。如果您的Block Device Drivers是Raid,或者SAN,NAS等存储设备,这些设备会更好地组织IO请求,不用IO调度器去做额外的调度工作
- 上层的应用程序比IO调度器更懂底层设备。或者说上层应用程序到达IO调度器的IO请求已经是它经过精心优化的,那么IO调度器就不需要画蛇添足,只需要按序执行上层传达下来的IO请求即可
- 对于一些非旋转磁头式的存储设备,使用Noop的效果更好。因为对于旋转磁头式的磁盘来说,IO调度器的请求重组要花费一定的CPU时间,但是对于SSD磁盘来说,这些重组IO请求的CPU时间可以节省下来,因为SSD提供了更智能的请求调度算法,不需要内核去画蛇添足。
DeadLine的吞吐量和响应时间都表现良好,适合用作数据库场景
CFQ 是通用服务器与桌面用户比较好的选择
附《高性能MySQL》里的相关部分:
Choosing a Disk Queue Scheduler
On GNU/Linux, the queue scheduler determines the order in which requests to a blockdevice are actually sent to the underlying device.
The default is Completely Fair Queueing, or cfq. It’s okay for casual use on laptops and desktops, where it helps preventI/O starvation, but it’s terrible for servers. It causes very poor response times under the types of workload that MySQL generates, because it stalls some requests in the queue needlessly.
The other two choices are suitable for server-class hardware, and in most cases they work about equally well. The noop scheduler is appropriate for devices that do their own scheduling behind the scenes, such as hardware RAID controllers and SANs, and deadline is fine both for RAID controllers and disks that are directly attached. Our benchmarks show very little difference between these two. The main thing is to use anything but cfq, which can cause severe performance problems.
查看与更改linux的IO调度算法
1.查看设备当前的 I/O 调度器:
cat /sys/block/sda/queue/scheduler
这里查看的是sda磁盘的调度器,不同磁盘的调度器可以单独进行设置
2.更改设备当前的I/O调度器:
echo noop > /sys/block/sda/queue/scheduler
参考资料
磁盘结构和读写
www.jianshu.com/p/20d0da96b…
通用块层
www.jianshu.com/p/ce43ec207…
Linux通用块设备层
lrita.github.io/images/post…
调整 Linux I/O 调度器优化系统性能
www.ibm.com/developerwo…
了解 Linux I/O 调度算法
jaminzhang.github.io/os/Understa…
Linux IO Scheduler(Linux IO 调度器)
www.cnblogs.com/cobbliu/p/5…
Linux IO调度器
developer.aliyun.com/article/265…
sda, sdb, sdc, sda1, sda2在Linux中都代表什么
www.cnblogs.com/awpatp/p/87…
磁盘I/O队列调度策略
www.cnblogs.com/bamanzi/p/l…