1. 背景
让我们梳理一下本系列文章整体脉络。
- 首先,Linux Block Driver - 1 介绍了一个只有 200 行源码的 Sampleblk 块驱动的实现。
- 然后,在 Linux Block Driver - 2 中,我们在 Sampleblk 驱动创建了 Ext4 文件系统,并做了一个
fio
顺序写测试。测试中我们利用 Linux 的各种跟踪工具,对这个fio
测试做了一个性能个性化分析。 - 而在 Linux Block Driver - 3 中,我们利用 Linux 跟踪工具和 Flamegraph 来对文件系统层面上的文件 IO 内部实现,有了一个概括性的了解。
本文将继续之前的实验,围绕这个简单的 fio
测试,探究 Linux 块设备驱动的运作机制。除非特别指明,本文中所有 Linux 内核源码引用都基于 4.6.0。其它内核版本可能会有较大差异。
2. 准备
阅读本文前,可能需要如下准备工作,
- 参考 Linux Block Driver - 1 中的内容,加载该驱动,格式化设备,装载 Ext4 文件系统。
- 按照 Linux Block Driver - 2 中的步骤,运行
fio
测试。
本文将在与前文完全相同 fio
测试负载下,在块设备层面对该测试做进一步的分析。
3. Block IO Pattern 分析
3.1 写请求大小
Linux 4.6 内核的块设备层的预定义了 19 个通用块层的 tracepoints。这些 tracepoints,可以通过如下 perf 命令来列出来,
$ sudo perf list block:*
List of pre-defined events (to be used in -e):
block:block_bio_backmerge [Tracepoint event]
block:block_bio_bounce [Tracepoint event]
block:block_bio_complete [Tracepoint event]
block:block_bio_frontmerge [Tracepoint event]
block:block_bio_queue [Tracepoint event]
block:block_bio_remap [Tracepoint event]
block:block_dirty_buffer [Tracepoint event]
block:block_getrq [Tracepoint event]
block:block_plug [Tracepoint event]
block:block_rq_abort [Tracepoint event]
block:block_rq_complete [Tracepoint event]
block:block_rq_insert [Tracepoint event]
block:block_rq_issue [Tracepoint event]
block:block_rq_remap [Tracepoint event]
block:block_rq_requeue [Tracepoint event]
block:block_sleeprq [Tracepoint event]
block:block_split [Tracepoint event]
block:block_touch_buffer [Tracepoint event]
block:block_unplug [Tracepoint event]
我们可以利用 block:block_rq_insert
来跟踪获取 fio
测试时,该进程写往块设备 /dev/sampleblk1 IO 请求的起始扇区地址和扇区数量,
$ sudo perf record -a -g --call-graph dwarf -e block:block_rq_insert sleep 10
因为我们指定了记录调用栈的信息,所以,perf script
可以获取 fio
从用户态到内核 block:block_rq_insert
tracepoint 的完整调用栈的信息。并且,给出了主次设备号,相关操作,及起始扇区和扇区数,
$ sudo perf script | head -n 20
fio 73790 [000] 1011438.379090: block:block_rq_insert: 253,1 W 0 () 3510 + 255 [fio]
5111e1 __elv_add_request (/lib/modules/4.6.0-rc3+/build/vmlinux)
518e64 blk_flush_plug_list (/lib/m