![](https://csdnimg.cn/release/download_crawler_static/87377764/bg1.jpg)
Linux 内核 IO 调度层
1
Linux内核IO调度层
http://www.ilinuxkernel.com
本文链接:http://ilinuxkernel.com/?p=1693
![](https://csdnimg.cn/release/download_crawler_static/87377764/bg2.jpg)
Linux 内核 IO 调度层
2
目 录
1 概述 ........................................................................................................................... 4
2 请求和请求队列 ......................................................................................................... 5
2.1 请求队列request_queue ...................................................................................... 6
2.2 请求 ................................................................................................................... 10
2.2.1 数据结构request .......................................................................................... 10
2.2.2 请求标志 ...................................................................................................... 13
2.3 调度器操作方法 ................................................................................................. 14
3 I/O调度器 ................................................................................................................. 15
3.1 I/O调度器的工作 ................................................................................................ 16
3.2 调度算法 ............................................................................................................ 16
3.2.1 CFQ调度器 .................................................................................................. 16
3.2.2 Deadline调度器 ............................................................................................ 17
3.2.3 I/O调度器的选择 .......................................................................................... 20
4 发送请求到I/O调度器 ............................................................................................... 20
4.1 __make_request() ......................................................................................... 20
4.2 __elv_add_request
() ..................................................................................... 24
4.3 __blk_run_queue() ........................................................................................ 27
4.4 驱动服务例程request_fn .................................................................................... 28
4.5 请求队列的由来 ................................................................................................. 29
4.5.1 块设备请求队列的创建 ................................................................................. 29
4.5.2 块设备请求队列的获取 ................................................................................. 31
5 plug与unplug设备 .................................................................................................... 31
5.1 blk_plug_device()与blk_remove_plug() .................................................... 32
5.2 generic_unplug_device()............................................................................... 33
6 块设备请求队列拥塞的处理 ..................................................................................... 34
7 常见问题 .................................................................................................................. 36
7.1 SSD经过I/O调度层吗? ..................................................................................... 36
7.2 I/O请求放到块设备请求队列上后,是否立即被执行呢? ................................... 37
![](https://csdnimg.cn/release/download_crawler_static/87377764/bg3.jpg)
Linux 内核 IO 调度层
3
图目录
图1 内核中块设备操作流程 ......................................................................................................... 4
图2 请求和请求队列 .................................................................................................................. 12
图3 NVMe SSD驱动架构 ........................................................................................................... 37
![](https://csdnimg.cn/release/download_crawler_static/87377764/bg4.jpg)
Linux 内核 IO 调度层
4
1 概述
我们仍以块设备操作流程开始,分析内核块设备操作中的过程。在块设备上的操作,涉
及内核中的多个组成部分,如图1所示。假设一个进程使用系统调用read()读取磁盘上的
文件。下面步骤是内核响应进程读请求的步骤;
图1 内核中块设备操作流程
(1)系统调用read()会触发相应的VFS(Virtual Filesystem Switch)函数,传递的参数
有文件描述符和文件偏移量。
(2)VFS确定请求的数据是否已经在内存缓冲区中;若数据不在内存中,确定如何执行读
操作。
Virtual Filesystem (VFS) Layer
Generic Block Layer
Disk
Filesystem
Buffer Cache (Page
I/O Scheduler Layer
Block Device Driver
Block Device Driver
Hard
Disk
Disk
Filesystem
SSD
Direct IO
Request Queue
… …
Request Queue
Kernel Space
Storage Media
![](https://csdnimg.cn/release/download_crawler_static/87377764/bg5.jpg)
Linux 内核 IO 调度层
5
(3)假设内核必须从块设备上读取数据,这样内核就必须确定数据在物理设备上的位置。
这由映射层(Mapping Layer)来完成。
(4)内核通过通用块设备层(Generic Block Layer)在 块 设备上执行读操作,启动I/O操作,
传输请求的数据。
(5)在通用块设备层之下是I/O调度层(I/O Scheduler Layer),根据内核的调度策略,
对等待的I/O等待队列排序。
(6)最后,块设备驱动(Block Device Driver)通过向磁盘控制器发送相应的命令,执行
真正的数据传输。
对于(1)、(2)两个步骤,在《Linux虚拟文件系统》中,我们讨论了VFS(Virtual Filesystem
Switch)主要数据结构和操作,结合相关系统调用(如sys_read()、sys_write()等)
的源码,我们不难理解VFS层相关的操作和实现。而对于第(3)步中的Mapping Layer主
要由具体的文件系统完成。
在《Linux通用块设备层》中,我们分析了第(4)步。上层的I/O请求发送到通用块设
备层(Generic Block Layer)后,就会将请求继续传送到I/O调度层(I/O Scheduler Layer)。
本文以2.6.32-279内核源码为基础,分析图1中的第5步,即I/O调度层。首先分析请求
队列(request queue)、请求(request)描述符;然后着重介绍两个个I/O调度算法;最
后分析内核中相关源码。
2 请求和请求队列
尽管块设备驱动可以一次传输一个扇区(sector),但块I/O层并不会对一个扇区执行
一次独立的I/O操作,因为在磁盘定位一个扇区的位置,是很耗时的工作,这会导致磁盘性
能的下降。取而代之的是,内核尽可能聚集多个扇区,作为一次操作,这样就减少磁头的移
动操作。
当内核读或写磁盘数据时,它就创建一个块设备请求(block device request),该请
求主要描述请求的扇区、操作的类型(读还是写)。然而,当请求创建后,内核并不是立即
执行请求,而是I/O操作仅是调度,且在稍后的时间才真正执行I/O操作。这种延迟是提高块