简单介绍下 Linux 中与 IO 相关的内容。
简介
可以通过如下命令查看与 IO 相关的系统信息。
# tune2fs -l /dev/sda7 ← 读取superblock信息
# blockdev --getbsz /dev/sda7 ← 获取block大小
# tune2fs -l /dev/sda7 | grep "Block size" ← 同上
# dumpe2fs /dev/sda7 | grep "Block size" ← 同上
# stat /boot/ | grep "IO Block" ← 同上
# fdisk -l ← 硬盘的扇区大小(Sector Size)
在 WiKi 中的定义:A “block”, a contiguous number of bytes, is the minimum unit of memory that is read from and written to a disk by a disk driver。
块是文件系统的抽象,而非磁盘的属性,一般是 Sector Size 的倍数;扇区大小则是磁盘的物理属性,它是磁盘设备寻址的最小单元。另外,内核中要求 Block_Size = Sector_Size * (2的n次方),且 Block_Size <= 内存的 Page_Size (页大小)。
磁盘类型
主要是要获取当前系统使用的什么类型的磁盘 (SCSI、IDE、SSD等),甚至是制造商、机器型号、序列号等信息。
$ dmesg | grep scsi
ioprofile 业务级
ioprofile 命令本质上等价于 lsof + strace,可以查看当前进程。
blktrace
blktrace 是块层 IO 路径监控和分析工具,作者 Jens Axboe 是内核 IO 模块的维护者,目前就职于 FusionIO,同时他还是著名 IO 评测工具 fio 的作者,使用它可以深入了解 IO 通路。
# yum install blktrace # 在CentOS中安装
$ make # 解压源码后直接安装
$ man -l doc/blktrace.8 # 查看帮助
其源码可以从 brick.kernel.dk 下载,详细使用参考 blktrace User Guide 。
原理
该工具包括了内核空间和用户空间两部分实现,内核空间里主要是给块层 IO 路径上的关键点添加 tracepoint,然后借助于 relayfs 系统特性将收集到的数据写到 buffer 去,再从用户空间去收集。
目前,内核空间部分的代码已经集成到主线代码里面去了,可以看看内核代码 block/blktrace.c
文件是不是存在,编译的时候把对应的这个 trace 选项选择上就可以了。
$ grep 'CONFIG_BLK_DEV_IO_TRACE' /boot/config-`uname -r`
大部分实现代码在 blktrace.c
,利用 tracepoint
特性,注册了一些 trace 关键点,可以查看 Documentation/tracepoint.txt
文件;交互机制利用了 relayfs 特性,看看 Documentation/filesystems/relay.txt
。
此时捞取的信息还比较原始,可以通过用户空间的 blkparse、btt、seekwatcher 这样的工具来分析收集到的数据。
注意,使用之前要确保 debugfs 已经挂载,默认会挂载在 /sys/kernel/debug
。
使用
典型的使用如下,其中 /dev/sdaa、/dev/sdc 作为 LVM volume adb3/vol。
# blktrace -d /dev/sda -o - | blkparse -i - -o blkparse.out # 简单用法,Ctrl-C退出
# btrace /dev/sda # 同上
# blktrace /dev/sdaa /dev/sdc & # 离线处理。1. 后台运行采集
% mkfs -t ext3 /dev/adb3/vol # 2. 做些IO操作
% kill -15 9713 # 3. 停止采集
% blkparse sdaa sdc sdo > events # 4. 解析后查看
在 blktrace 中,-d
表示监控哪个设备,-o
表示将监控输出到标准输出;在 blkparse 中,-i
表示从标准输入获取信息,-o
表示将解析的内容记录在 blkparse.out 。
如下是输出的详细信息。
其中 event 对应了事件表;后面一列代表了操作类型,包括了 R(read)、W(write)、B(barrier operation)、S(synchronous operation),其中 event 有如下类型:
事件 | 说明 | 源码(block目录下) SetPosition |
---|---|---|
A | IO was remapped to a different device | blk-core.c/trace_block_remap |
B | IO bounced | bounce.c/trace_block_bio_bounce |
C | IO completion | blk-core.c/trace_block_rq_complete |
D | IO issued to driver | elevator.c/trace_block_rq_issue |
F | IO front merged with request on queue | blk-core.c/trace_block_bio_frontmerge |
G | Get request | blk-core.c/trace_block_getrq |
I | IO inserted onto request queue | elevator.c/trace_block_rq_insert |
M | IO back merged with request on queue | blk-core.c/trace_block_bio_backmerge |
P | Plug request | blk-core.c/trace_block_plug |
Q | IO handled by request queue code | blk-core.c/trace_block_bio_queue |
S | Sleep request | blk-core.c/trace_block_sleeprq |
T | Unplug due to timeout | blk-core.c/trace_block_unplug_timer |
U | Unplug request | blk-core.c/trace_block_unplug_io |
X | Split | bio.c/trace_block_split |
详解
仍以如下简单命令为例。
$ blktrace -d /dev/sda -o sda # 输出 sda.blktrace.N 文件,N 为物理 CPU 个数。
$ ls /sys/kernel/debug/block/sda # 查看debugfs中的文件
dropped msg trace0 trace1 trace2 trace3
$ blkparse -i sda.blktrace.0 # 解析成可读内容
$ blkrawverify sda # 校验,其中sda为blktrace的-o选项
其中 blktrace 通过 ioctl() 执行 BLKTRACESETUP、BLKTRACESTART、BLKTRACESTOP、BLKTRACETEARDOWN 操作,此时会在 debugfs 目录的 block/DEV 目录下写入数据。
FIO
FIO 是个非常强大的 IO 性能测试工具,其作者 Jens Axboe 是 Linux 内核 IO 部分的 maintainer,可以毫不夸张的说,如果你把所有的 FIO 参数都搞明白了,基本上就把 Linux IO 协议栈的问题搞的差不多明白了。
一个 IO 压测工具,源码以及二进制文件可以参考 github-axboe,或者直接从 freecode.com 上下载。另外,该工具同时提供了一个图形界面 gfio 。
在 CentOS 中可以通过如下方式安装。
# yum --enablerepo=epel install fio
源码编译
可以直接从 github 上下载源码,然后通过如下方式进行编译。
----- 编译,注意依赖libaio
$ make
----- 查看帮助
$ man -l fio.1
----- 通过命令行指定参数,进行简单测试
$ fio --name=global --rw=randread --size=128m --name=job1 --name=job2
----- 也可以通过配置文件进行测试
$ cat foobar.fio
[global]
rw=randread
size=128m
[job1]
[job2]
$ fio foobar.fio
可以通过命令行启动,不过此时参数较多,可以使用配置文件。
源码解析
其版本通过 FIO_VERSION 宏定义,并通过 fio_version_string 变量定义。
main()
|-parse_options()
| |-parse_cmd_line() 解析命令行,如-i显示所有的ioengines
| | `-add_job() file1: xxxxxx 打印job信息
| `-log_info() fio-2.10.0
`-fio_backend()
|-create_disk_util_thread() 用于实时显示状态
| |-setup_disk_util()
| `-disk_thread_main() 通过pthread创建线程
| `-print_thread_status()
|
|-run_threads() Starting N processes
| |-setup_files() Laying out IO file(s)
| |-pthread_create() 如果配置使用线程,调用thread_main
| `-fork() 或者调用创建进程,同样为thread_main
|
|-show_run_stats()
`-show_thread_status_normal() 用于显示最终的状态
|-show_latencies() 显示lat信息
`-... ... CPU、IO depth
ioengines 通过 fio_libaio_register() 类似的函数初始化。
其它
ionice
获取或设置程序的 IO 调度与优先级。
ionice [-c class] [-n level] [-t] -p PID...
ionice [-c class] [-n level] [-t] COMMAND [ARG]
----- 获取进程ID为89、91的IO优先级
$ ionice -p 89 91
参考
Block IO Layer Tracing: blktrace 介绍 blktrace 命令的使用;关于内核的 trace 功能参考 Kernel Trace Systems 。