背景
- 在嵌入式平台实现行车记录仪等产品的录像功能(使用SD卡存储)时,性能普通,研究确认IO调度是否能提升性能。
调度说明
- 在linux平台以普通方式(缓存io)读写文件,读写请求并不是直接发给存储设备处理,操作系统会先将请求(数据和目标等信息)缓存起来,经过算法调度和处理(合并、排序请求)后,再发送请求到存储设备。
为何需要调度算法
- 磁盘是低速设备,为了加快磁盘读写性能,系统会使用内存作为缓存和缓冲,当内存的读写请求较多时就会引发调度问题。
- IO请求较多以及来源(请求发起进程)比较复杂时。
- 为了保证所有IO请求能够以相对公平的方式得到执行,从而产生的优先级问题。
- 为了尽可能的提高性能,需要配合缓存机制实现一些优化策略,例如:丢弃重复的请求(两个请求先后写同一block,只需执行后面的请求),取消无必要读写请求(一个请求写,一个请求再读取同样的文件,读操作就可以不必触发磁盘IO,可以直接从缓存中读取),合并相临块操作请求(两个请求读写相邻block,可以合并成一个io请求,减少存储设备请求次数)。
- 存储设备特性
- 机械硬盘的磁头是通过不断旋转来读写数据的,定位数据时,可能出现寻道时间较长的问题,特别是请求操作的磁盘块与磁头移动方向相反时,需要等磁头转完一圈,耗时无法忽视。
调度措施
- 请求队列
- 请求队列对性能影响较大,队列容量越大,可缓存的请求越多,可以进行更好的合并和排序,但是队列越大,写的实时性越差。
/sys/block/[存储设备]/queue/nr_requests
例子:
xxx:$ cat /sys/block/sda/queue/nr_requests
64
- IO调度算法通过两种方式来提升性能:合并和排序。
- 合并:当多个IO请求操作的是相邻的磁盘扇区,那么就将这些请求合并为一个请求,cpu只需要向磁盘发送一个请求指令,减少了请求指令的开销。
- 排序:将合并处理后的IO请求,根据请求磁盘扇区的顺序,在请求队列中进行排序,使得磁头可以按照磁盘的旋转顺序完成IO请求,可以减少磁盘的寻道次数。
个人总结
- IO调度适用于IO请求比较多,比较复杂的场景,例如:服务器应用,或者使用机械硬盘的情况下。
- 在嵌入式平台,IO请求情况比较简单,特别是使用SD卡或者固态硬盘等作为存储设备的情况下,调整调度算法的作用不大。
调度算法
- 为了满足不同的需求,linux内核支持多种io调度算法。
Noop (No Operation)
- Noop是内核中最原始、最简单的调度算法,该算法将IO请求放入到一个FIFO队列中,对于一些连续磁盘的IO请求,Noop算法会适当做一些合并,然后逐个执行这些IO请求。
- 在以下场景中会有一些优势
- 如今的存储设备有些比较智能,拥有自己的调度能力可以更好更合适地组织IO请求,不像早期存储设备只能被动的执行IO请求,例如:Raid,SAN,NAS等,不需要IO调度器去做额外的调度工作。
- 嵌入式平台固态存储设备。
CFQ(Complete Fair Queuing)
- CFQ 为每个进程分配一个 I/O请求队列,在每个队列的内部,进行合并和排序的优化,CFQ以轮询的方式处理这些队列,每次从一个队列中处理特定数量的请求(默认为 4个),它试图将 I/O 带宽均匀的分配给每个进程。
- CFQ 原本针对的是多媒体或桌面应用场景,然而,CFQ在许多场景中内表现的很好; CFQ 对每一个 IO 请求都是公平的。这使得 CFQ很适合离散读的应用 (eg: OLTP DB)。
- 多数Linux发型版(不仅仅是企业版)选择 CFQ 作为默认的 I/O 调度器。
Deadline
内核相关配置
- 查看内核支持的io调度算法
~# dmesg | grep -i "io scheduler"
[ 1.018721] io scheduler noop registered
[ 1.018725] io scheduler deadline registered
[ 1.018752] io scheduler cfq registered (default)
[ 1.018756] io scheduler mq-deadline registered
[ 1.018759] io scheduler kyber registered
- 注意:加(default)的项表示为默认的io调度算法。
- 查看和设置某个块设备使用的调度算法
* 查看
cat /sys/block/sda/queue/scheduler
noop [deadline] cfq
说明: /dev/sda 是一个块设备,带中括号[]的是设备当前使用的调度算法
* 设置
echo cfq > /sys/block/sda/queue/scheduler
- 设置内核默认的调度算法
- 有些嵌入式linux环境下设置不了设备使用的调度算法,所以只能更改默认的调度算法
* 修改内核配置选项
CONFIG_DEFAULT_NOOP=y
CONFIG_DEFAULT_IOSCHED="noop"