选择 Linux I/O 调度程序

据说可以通过写入/sys/block/[ disk ]/queue/Scheder 来更改正在运行的内核上某个特定设备的 I/O 调度程序。例如,我可以在我的系统上看到:

anon@anon:~$ cat /sys/block/sda/queue/scheduler
noop anticipatory deadline [cfq]

默认值是完全公平的排队计划程序。我想知道的是,在我的自定义内核中包含所有四个调度程序是否有用。除非内核足够聪明,能够为正确的硬件选择正确的调度器,特别是闪存驱动器的“ noop”调度器和传统硬盘驱动器的其他调度器,否则似乎没有多大意义。

是这样吗?

148500 次浏览

The aim of having the kernel support different ones is that you can try them out without a reboot; you can then run test workloads through the sytsem, measure performance, and then make that the standard one for your app.

On modern server-grade hardware, only the noop one appears to be at all useful. The others seem slower in my tests.

As documented in /usr/src/linux/Documentation/block/switching-sched.txt, the I/O scheduler on any particular block device can be changed at runtime. There may be some latency as the previous scheduler's requests are all flushed before bringing the new scheduler into use, but it can be changed without problems even while the device is under heavy use.

# cat /sys/block/hda/queue/scheduler
noop deadline [cfq]
# echo anticipatory > /sys/block/hda/queue/scheduler
# cat /sys/block/hda/queue/scheduler
noop [deadline] cfq

Ideally, there would be a single scheduler to satisfy all needs. It doesn't seem to exist yet. The kernel often doesn't have enough knowledge to choose the best scheduler for your workload:

  • noop is often the best choice for memory-backed block devices (e.g. ramdisks) and other non-rotational media (flash) where trying to reschedule I/O is a waste of resources
  • deadline is a lightweight scheduler which tries to put a hard limit on latency
  • cfq tries to maintain system-wide fairness of I/O bandwidth

The default was anticipatory for a long time, and it received a lot of tuning, but was removed in 2.6.33 (early 2010). cfq became the default some while ago, as its performance is reasonable and fairness is a good goal for multi-user systems (and even single-user desktops). For some scenarios -- databases are often used as examples, as they tend to already have their own peculiar scheduling and access patterns, and are often the most important service (so who cares about fairness?) -- anticipatory has a long history of being tunable for best performance on these workloads, and deadline very quickly passes all requests through to the underlying device.

The Linux Kernel does not automatically change the IO Scheduler at run-time. By this I mean, the Linux kernel, as of today, is not able to automatically choose an "optimal" scheduler depending on the type of secondary storage devise. During start-up, or during run-time, it is possible to change the IO scheduler manually.

The default scheduler is chosen at start-up based on the contents in the file located at /linux-2.6 /block/Kconfig.iosched. However, it is possible to change the IO scheduler during run-time by echoing a valid scheduler name into the file located at /sys/block/[DEV]/queue/scheduler. For example, echo deadline > /sys/block/hda/queue/scheduler

It's possible to use a udev rule to let the system decide on the scheduler based on some characteristics of the hw.
An example udev rule for SSDs and other non-rotational drives might look like

# set noop scheduler for non-rotating disks
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="noop"

inside a new udev rules file (e.g., /etc/udev/rules.d/60-ssd-scheduler.rules). This answer is based on the debian wiki

To check whether ssd disks would use the rule, it's possible to check for the trigger attribute in advance:

for f in /sys/block/sd?/queue/rotational; do printf "$f "; cat $f; done

You can set this at boot by adding the "elevator" parameter to the kernel cmdline (such as in grub.cfg)

Example:

elevator=deadline

This will make "deadline" the default I/O scheduler for all block devices.

If you'd like to query or change the scheduler after the system has booted, or would like to use a different scheduler for a specific block device, I recommend installing and use the tool ioschedset to make this easy.

https://github.com/kata198/ioschedset

If you're on Archlinux it's available in aur:

https://aur.archlinux.org/packages/ioschedset

Some example usage:

# Get i/o scheduler for all block devices
[username@hostname ~]$ io-get-sched
sda:    bfq
sr0:    bfq


# Query available I/O schedulers
[username@hostname ~]$ io-set-sched --list
mq-deadline kyber bfq none


# Set sda to use "kyber"
[username@hostname ~]$ io-set-sched kyber /dev/sda
Must be root to set IO Scheduler. Rerunning under sudo...


[sudo] password for username:
+ Successfully set sda to 'kyber'!


# Get i/o scheduler for all block devices to assert change
[username@hostname ~]$ io-get-sched
sda:    kyber
sr0:    bfq


# Set all block devices to use 'deadline' i/o scheduler
[username@hostname ~]$ io-set-sched deadline
Must be root to set IO Scheduler. Rerunning under sudo...


+ Successfully set sda to 'deadline'!
+ Successfully set sr0 to 'deadline'!


# Get the current block scheduler just for sda
[username@hostname ~]$ io-get-sched sda
sda:    mq-deadline

Usage should be self-explanatory. The tools are standalone and only require bash.

Hope this helps!

EDIT: Disclaimer, these are scripts I wrote.