Linux I/O 调度器

Linux I/O 调度器

Linux I/O 调度器是控制内核提交读写请求给磁盘的方式. Linux 支持三种 I/O 电梯调度算法, 分别为 CFQ, Noop, Deadline 三种. 简单介绍下这三种.

CFQ (Completely Faire Scheduler)

完全公平调度器 (CFQ) 自 kernel2.6.18 后成为了 Linux 默认的 I/O 调度器, 它也是大多数 Linux 发行版的默认调度器. CFQ 调度器的目的是为所有请求 I/O 操作的进程提供一个公平的磁盘 I/O 带宽分配.

CFQ 调度器为每个进程单独创建一个同步 I/O 队列来管理其同步的请求然后为每个队列分配访问磁盘的时间片. 时间片长度和队列允许提交的请求数取决于其进程的 I/O 优先级. 以此保证每个进程的 I/O 资源占用是公平的.

CFQ 算法的特点是按照 I/O 请求的地址进行排序, 而不是按照先来后到的顺序进行响应. 这样减少了不必要的磁盘寻道, 提高了吞吐量. 但也意味着, 先来的 I/O 请求可能不能尽快得到响应, 可能出现饿死现象.

Deadline

Deadline 调度器主要目的是保证请求的启动服务时间. 它在所有的 I/O 请求上加上一个期限来防止请求饿死. 除了本身提供的 I/O 排队队列之外, Deadline 额外分为为读 I/O 和写 I/O 提供了 FIFO 队列. 排序队列按照按照扇区数排序, FIFO 队列按照截止时间排序. FIFO 队列优先级高于排队队列, 读队列优先级高于写队列. 排序队列使用红黑树结构, 而 FIFO 使用链表结构. 读 FIFO 队列的最大等待时间为 500ms, 写FIFO队列的最大等待时间为 5s.

Deadline 算法会在写队列没有发生饥饿的情况下处理读队列, 在进入读队列处理时, 会首先检查 FIFO 读队列查看是否有过期请求, 如果有就优先处理它. 如果前面写队列出现饥饿, 意即 FIFO 写队列有过期请求, 它就会进入写队列处理然后处理 FIFO 中过期的写请求.

Deadline 本质还是基于 CFQ 的, 只不过加了两个 FIFO 队列来避免饿死问题.

Deadline 调度器更适合用于多线程处理环境和数据库环境. 避免了 CFQ 调度器的饿死问题.

Noop

Noop 调度器非常简单, 它提供了一个最简单的 FIFO 队列并对请求进行简单的合并处理. 当确定不需要根据扇区号来对请求排序时, 这个调度器是非常高效的. 比较适合在 SSD 上面应用.

总的来看, Noop 更适合固态硬盘. Deadline 适合需要优先保证读操作且避免饿死的情况如数据库场景. CFQ 比较复杂, 容易出现饿死问题, 但保证了尽可能的公平. 推荐固态硬盘下可以考虑把默认的 CFQ 调度器改为 Noop 调度器或者 Deadline 调度器.

更换 Linux I/O 调度器

可以通过如下命令查看你当前的 I/O 调度器:

cat /sys/block/sda/queue/scheduler

如果想修改的话, 可以先进入 su 然后执行:

echo noop > /sys/block/sda/queue/scheduler

可以把 noop 换位 cfq 或者 deadline.

这个更改会立即生效, 但重启会失效. 如果想设为永久, 在 Arch Linux 上面可以如下操作:

% cat /etc/udev/rules.d/60-scheduler.rules
# Set deadline scheduler for non-rotational disks
ACTION=="add|change", KERNEL=="sd[a-z]", TEST!="queue/rotational", ATTR{queue/scheduler}="deadline"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="deadline"

/etc/udev/rules.d/60-scheduler.rules (没有的话新建) 加上上面两行, 然后再运行该命令:

mkinitcpio -p linux

这样就可以了~


参考资料: