《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #13 使用Block I/O控制器设置I/O优先级

简介: 本节书摘来自华章出版社《Linux内核精髓:精通Linux内核必会的75个绝技》一书中的第2章,第2.7节,作者 竹部 晶雄、平松 雅巳,更多章节内容可以访问云栖社区“华章计算机”公众号查看

HACK #13 使用Block I/O控制器设置I/O优先级

本节介绍使用Block I/O控制器的功能设置I/O优先级的方法。
Block I/O控制器可以将任意进程分组,并对该分组设置I/O的优先级。这个功能是在Linux 2.6.33时添加到Linux内核中的。例如,在前台进行一般处理的同时,在后台磁盘备份处理的情况下,如果备份处理频繁地向磁盘进行I/O操作,前台的处理即使有I/O请求,也不能立刻进行I/O处理,结果导致前台处理的性能下降。
Block I/O控制器在这种情况下就非常有效。创建I/O优先级较高的分组和较低的分组,并将前台处理的进程和后台处理的进程分别分配到这些分组中。这样可以使前台处理的I/O优先于后台处理,防止性能下降。
本节将介绍Block I/O控制器的使用方法。使用的Linux内核版本为2.6.35。
使用Block I/O控制器的前提条件
Block I/O控制器是Cgroup的子系统之一,是作为I/O调度程序之一的CFQ的一部分安装的。因此,使用Block I/O控制器时,必须使用启用了下列config选项编译的内核。

CONFIG_BLK_CGROUP
CONFIG_CFQ_GROUP_IOSCHED

确认Cgroup支持
首先确认运行中的内核是否支持Cgroup和Cgroup子系统Block I/O控制器。如果有/proc/cgroups,运行中的内核就可以支持Cgroup。/proc/cgroups的内容如下。

$ cat /proc/cgroups
    subsys_name    hierarchy    num_cgroups    enabled
    blkio                1        1        1

只要subsys_name列显示了blkio,且enabled为1就没问题。如果看不到blkio,就需要重新编译内核。如果enabled为0,则启动时的内核命令行应当如下所示。

cgroup_disabled=blkio

这时需要修改/boot/grub/grub.conf等,将上述指定从内核命令行中删除,并重新启动。
确认CFQ支持
接下来确认是否能够将CFQ作为I/O调度程序使用。例如,想要查看块设备sdb中可以使用的I/O调度程序时,需要执行下列命令。

$ cat /sys/class/block/sdb/queue/scheduler
   noop deadline [cfq]

可以使用的I/O调度程序会显示出来,其中当前选择的调度程序已加上了方括号。如果显示cfq则没问题;如果不显示就需要启用CFQ,重新编译内核。
注意事项:设备种类不同,scheduler文件的内容也不同。I/O调度程序是对一般的块设备使用的,因此,例如在loopback设备loop0等中不会显示cfq。请对sda、sdb等一般的块设备进行确认。
尝试使用Block I/O控制器
Block I/O控制器的设置通过cgroup文件系统进行。关于Cgroup和cgroup文件系统的基本情况请参考Hack #7。
首先挂载cgroup文件。将blkio作为挂载选项,指定将Block I/O控制器作为子系统使用。

# mount -t cgroup -o blkio cgroup/cgroup

然后,在CFQ中为想要控制I/O优先级的块设备设置I/O调度程序。这里使用的块设备是sdb。

# echo cfq > /sys/class/block/sdb/queue/scheduler
$ cat /sys/class/block/sdb/queue/scheduler
    noop deadline [cfq]

这时,使用Block I/O控制器前的准备工作就完成了。为了方便讲解,在这里创建优先级较高的分组“high”和优先级较低的分组“low”,并分别向各分组分配1个进程来观察I/O的情况。首先创建分组。

# mkdir /cgroup/high
# mkdir /cgroup/low

对各分组设置I/O的优先级。设置优先级时,在blkio.weight中写入100~1000的“weight值”。初始值为500,值越大表示优先级越高。

# echo 1000 > /cgroup/high/blkio.weight
# echo 100 > /cgroup/low/blkio.weight

小贴士:仅根分组的weight初始值为1000。
为了进行测试,制作一个简单的脚本。这个脚本将进程添加到所指定的两个分组low和high,分别计算出读文件所需的时间。创建如下的“blkio_test.sh”脚本,并为其赋予执行权限。

#! /bin/sh

# blkio_test.sh
#     Test script for Block IO Controller
#     read /mnt/sdb/low.dat ($flow) as cgroup $cg_low
#     and read /mnt/sdb/high.dat ($fhigh) as cgroup $cg_high simultaneously.
#
#     $1: cgroup path for lower priority (--> $cg_low)
#     $2: cgroup path for higher priority (--> $cg_high)


function print_usage()
{
        echo "usage: $0 <cgroup_low> <cgroup_high>"
        exit 1
}


##############################
# params and variables

flow=/mnt/sdb/low.dat
fhigh=/mnt/sdb/high.dat

# cgroups to which processes will be asigned

if [ $# != 2 ]; then
        print_usage
fi

cg_low=$1
cg_high=$2

for cg in $cg_low $cg_high; do
        if [ ! -d $cg ]; then
                echo "$cg does not exists"
                print_usage
        fi
done

# temporary files
out_low=$(mktemp)
out_high=$(mktemp)


##############################
# sync, drop caches and read files

echo -n "sync and drop all caches..."
sync
echo 3 > /proc/sys/vm/drop_caches
echo "done"

echo -n "reading files..."
echo 
$$
 > $cg_low/tasks
(time dd if=$flow of=/dev/null) > $out_low 2>&1 &

echo 
$$
 > $cg_high/tasks
(time dd if=$fhigh of=/dev/null) > $out_high 2>&1 &

wait
echo "done"


##############################
# print the results

echo "--------------------"
echo "dd in $cg_low:"
cat $out_low
echo "--------------------"
echo "dd in $cg_high:"
cat $out_high

rm -f $out_low $out_high

首先,在不分组的情况下进行测试。使用同属于根分组的两个进程,同时读入文件。由于脚本读入的是/mnt/sdb/low.dat、/mnt/sdb/high.dat文件,因此需要先创建两个大小适当的文件,再运行脚本。这里创建了约400MB的文件并执行相关代码。

# dd if=/dev/zero of=/mnt/sdb/low.dat bs=1M count=400
# dd if=/dev/zero of=/mnt/sdb/high.dat bs=1M count=400
# ./blkio_test.sh /cgroup /cgroup
--------------------
dd in /mnt/cgroups:
819200+0 records in
819200+0 records out
419430400 bytes (419 MB) copied, 14.0493 s, 29.9 MB/s

real    0m14.156s
user    0m0.261s
sys    0m1.183s
--------------------
dd in /mnt/cgroups:
819200+0 records in
819200+0 records out
419430400 bytes (419 MB) copied, 14.2007 s, 29.5 MB/s

real    0m14.292s
user    0m0.281s
sys    0m1.186s

两个进程同样使用约14秒的时间完成读入。接下来,在分组的情况下进行测试。将两个进程分别添加到low、high分组中,同时读入文件。

# ./blkio_test.sh /cgroup/low /cgroup/high
--------------------
dd in /cgroup/low:
819200+0 records in
819200+0 records out
419430400 bytes (419 MB) copied, 13.0829 s, 32.1 MB/s

real    0m13.388s
user    0m0.250s
sys    0m1.208s
--------------------
dd in /cgroup/high:
819200+0 records in
819200+0 records out
419430400 bytes (419 MB) copied, 7.43459 s, 56.4 MB/s

real    0m7.818s
user    0m0.256s
sys    0m1.209s

属于/cgroup/low的进程完成文件读入耗费约13秒,而属于/cgroup/high的进程完成读入耗费约8秒。与优先级较低,即weight设置值较小的分组相比,优先级较高,即属于weight设置值较大的分组(/cgroup/high)的进程可以优先执行I/O操作。
Block I/O控制器提供的特殊文件
除了blkio.weight以外,Block I/O控制器还提供了一些其他的特殊文件。文件列表如表2-7所示。只读属性的特殊文件是用来获取统计信息的文件,多数是根据各设备、I/O的类型(read/write、sync/async)另起一行的。
表2-7 Block I/O控制器的设置用特殊文件
image

小贴士:I/O的合并,是指将应用程序发出的多个I/O请求合并为1个。把相邻扇区的I/O整理到一起后,仅需一次DMA就可以完成I/O,因此可以提高I/O处理的效率。
另外,在启用内核选项CONFIG_DEBUG_BLK_CGROUP进行编译的内核中,还有为用户提供调试用信息的文件,这里不作说明。
关于Block I/O控制器的CFQ设置用虚拟文件
/sys/block//queue/iosched中有CFQ的设置用虚拟文件,表2-8所示文件会对Block I/O控制器的运行产生影响。
表2-8 关于Block I/O控制器的sysfs的CFQ设置文件
image

限制事项
由于Block I/O控制器仍是比较新的功能,因此在使用上还有一些限制。接下来列出了到Linux内核版本2.6.35为止存在的限制事项。
不支持非同步I/O
需要各分组进行优先级控制的,当前仅为同步I/O,即初次读入和Direct I/O的读写。普通的写入是经过页面缓存的非同步I/O,因此不属于优先级控制的对象,都被看做根分组发出的I/O。
不支持分组层次化
把分组的层次限制为仅一层,因此无法创建从根分组开始有两层次以上的分组。用cgroup文件系统的目录层次可以显示如下。

/cgroup         # 根分组
/cgroup/gr1      # 第一层:可以创建
/cgroup/gr1/gr2   # 第二层:不可以创建

根分组与子分组作同等处理
根分组不作为其他子分组的上级分组,而是作为同等分组进行处理。当根分组内存在拥有实时I/O优先权的进程时,这个影响比较明显。根分组会被其他子分组抢占,因此即使是实时进程,也不能占用I/O带宽。
Block I/O控制器的结构与注意事项
为了更准确地理解Block I/O控制器的运行,下面介绍其内部结构及其使用上的注意事项。
Block I/O控制器对各分组的I/O请求分配时间片。仅许可各分组在这个时间片内执行I/O操作。分组的weight值越大,时间片的长度越长;weight值越小,时间片的长度越短。通过这种方式来指定分组间的I/O操作的优先级。
这里需要注意的是,优先级不是通过I/O带宽(字节/秒,bps),而是通过时间片的长度来指定的。因此,两个分组在I/O完成所需时间差距极大的模式下执行I/O时,具体来说,就是一个依次读入,另一个随机读入的情况下,各分组的I/O带宽就会与weight值的设置迥然不同。
另外,还需要注意的是,Block I/O控制器只有在针对设备的I/O发生竞争时,才会根据优先级对I/O进行控制。在I/O不发生竞争的情况下,即使是优先级较低的分组,Block I/O控制器也不会禁止I/O的执行。也就是说,如果优先级较高的分组没有对某个设备发出I/O请求,那么即使是优先级较低的分组,也可以使用该设备的全部带宽。这里所说的设备是指实际的物理块设备。dm(device-mapper)等可以为应用程序提供逻辑性块设备,但是Block I/O控制器与逻辑块设备完全无关,只在向物理设备执行I/O时进行控制。当多个分组同时对同一逻辑设备发出I/O请求时,根据这些I/O请求所针对的物理设备不同,实际的I/O有可能竞争,有可能不竞争。这时,想要预测出哪个I/O会优先进行,就必须正确把握逻辑设备和物理设备的对应关系。
小结
Block I/O控制器是正在开发的新功能。还存在前面所述的一些限制,因此更需要大家通过实际体验来发现存在问题或尚有不足的功能,反馈给开发者或者自己大胆地制作出来,才能够将其不断地完善。
参考文献
关于Cgroup请参考Hack #7。
Documentation/cgroups/blkio-controller.txt(内核源文档)
—Munehiro IKEDA

相关文章
|
7天前
|
监控 Oracle 关系型数据库
Linux平台Oracle开机自启动设置
【11月更文挑战第8天】在 Linux 平台设置 Oracle 开机自启动有多种方法,本文以 CentOS 为例,介绍了两种常见方法:使用 `rc.local` 文件(较简单但不推荐用于生产环境)和使用 `systemd` 服务(推荐)。具体步骤包括编写启动脚本、赋予执行权限、配置 `rc.local` 或创建 `systemd` 服务单元文件,并设置开机自启动。通过 `systemd` 方式可以更好地与系统启动过程集成,更规范和可靠。
|
4天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
21 4
|
8天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
28 6
|
6天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
27 9
|
5天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
23 6
|
6天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
23 5
|
6天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
7天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
6天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
6天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
22 2