简介:fio-2.2.10.tar.gz 是面向 Linux 系统管理员、测试工程师与存储性能调优人员的开源 I/O 基准测试工具源码包,用于评估硬盘、SSD、网络存储等设备的读写性能,帮助定位存储瓶颈、验证存储方案效能。压缩包共 342 个文件,约 573KB,以 125 个 c 源文件与 146 个 h 头文件为主体,构成核心测试引擎;另有 38 个 fio 作业配置文件、makefile 与 configure 构建脚本、manpage 手册及 gnuplot 绘图脚本等,覆盖编译、配置、运行与结果可视化全流程。该版本支持多线程并发、随机与顺序混合读写、队列深度调节,并可输出 CSV、JSON 等报告格式,便于导入分析工具深入解读。目前已有 409 人学习下载,适合需要掌握 IOPS、吞吐量与延迟等关键指标、开展存储性能测试与调优实践的读者参考使用。
1. fio-2.2.10.tar.gz:一个老版本压测工具的翻新用法
拿到fio-2.2.10.tar.gz这个包,第一反应往往是「怎么是个老版本」。确实,fio 早已更新到 3.x 系列,2.2.10 属于十多年前的产物。但现实里它出现的频率并不低:一些内网镜像站、嵌入式 SDK、老发行版的源码仓库里,这个 tar.gz 还静静躺着。问题在于,很多人解压后直接./configure && make,结果在较新的编译器上翻车,或者跑起来发现某些参数行为和文档对不上。
这篇笔记要解决的就是这件事:把 fio-2.2.10.tar.gz 从解压到跑出一份可信的磁盘/文件系统压测报告,完整走一遍。适合两类人——手里只有这个老包、又必须用它做 IO 基准测试的运维和存储工程师;以及想搞懂 fio 参数到底怎么影响结果、不想被新版本封装层遮住细节的开发者。核心不是教你「fio 是什么」,而是让这个特定版本的 tar.gz 在你机器上真正跑起来、结果能看、坑能绕开。
2. 解压、编译与依赖:让 2.2.10 在现代系统上活过来
2.1 先看清包里有什么,再决定编译路径
fio-2.2.10.tar.gz是标准 GNU Autotools 工程,解压后目录结构大致是configure、Makefile.in、fio.c、engines/、os/这些。先别急着 configure,用两条命令确认环境和包内容:
tar -tzf fio-2.2.10.tar.gz | head -30 tar -xzf fio-2.2.10.tar.gz && cd fio-2.2.10 ls -la第一条列出压缩包前 30 个条目,确认没有路径穿越、没有奇怪的顶层目录嵌套;第二条解压并进入。ls -la要重点看有没有configure和Makefile.in,如果只有Makefile而没有configure,说明这个包可能是二次打包的,编译方式完全不同。
2.2.10 的configure脚本生成于老版本 autoconf,在新系统上可能报cannot find install-sh或aclocal相关错误。常见做法是先补上基础构建链:
sudo apt-get install -y build-essential autoconf automake libtool # CentOS/RHEL 系用:sudo yum groupinstall "Development Tools"这里build-essential提供 gcc、make、libc 头文件;autoconf/automake/libtool是为了在configure失效时能重新生成。注意 2.2.10 依赖的是老式 autotools 语法,新版 automake 可能不兼容,所以优先尝试直接用包内自带的configure,而不是一上来就autoreconf。
2.2 configure 的关键开关与参数含义
直接./configure通常能过,但有几个开关决定后面能不能测到你想测的东西:
./configure \ --prefix=/usr/local/fio-2.2.10 \ --disable-native \ --enable-libaio \ --enable-gfio 2>&1 | tee configure.log--prefix:安装到独立目录,避免覆盖系统里可能存在的其他 fio 版本,这对老版本尤其重要。--disable-native:关闭针对本机 CPU 的激进优化。老版本在-march=native下有时会生成非法指令,尤其在跨机器编译时。--enable-libaio:启用 Linux 原生异步 IO 引擎。如果你要测的是ioengine=libaio,这个必须开,否则 configure 会静默跳过,运行时才报 engine not found。--enable-gfio:带图形化输出支持,依赖 gtk/glib。如果机器上没有桌面环境,这个开关会导致 configure 失败,可以去掉。
configure 结束后看configure.log末尾的 summary,确认libaio显示为 yes。如果显示 no,检查libaio-devel是否安装:
sudo apt-get install -y libaio-dev # 或 sudo yum install -y libaio-devel2.3 编译报错的三类典型修法
2.2.10 在新 gcc(9 以上)上编译,最容易撞三类错误。
第一类是-Werror导致的警告升级为错误,现象是某个.c文件里implicit declaration of function直接中断。解决是去掉 Werror:
make CFLAGS="-O2 -g -Wno-error" 2>&1 | tee make.log第二类是getrandom或clock_gettime链接失败,报undefined reference。这是老版本没链接-lrt,手动补:
make LDFLAGS="-lrt -lpthread" 2>&1 | tee make.log第三类是os/目录下平台判断出错,比如在较新的内核头文件上sys/ioctl.h冲突。这种情况优先看make.log里第一个 error 出现的文件,而不是最后一个,因为后续错误往往是连锁的。
编译成功后make install,然后验证:
/usr/local/fio-2.2.10/bin/fio --version输出应类似fio-2.2.10。如果报error while loading shared libraries,执行ldd看缺哪个 so,通常是 libaio,补装运行时库即可。
3. 用 fio-2.2.10 跑出第一份可信的 IO 报告
3.1 最小可用 job 文件与每个参数的作用
fio 的用法是「一个 job 文件描述一次压测」。2.2.10 的 job 语法和 3.x 基本一致,但个别参数名有差异。先写一个最小可用的顺序读测试:
[global] ioengine=libaio direct=1 runtime=30 time_based=1 group_reporting=1 filename=/data/testfile size=2G [seq-read] rw=read bs=128k iodepth=16 numjobs=1逐项说明:
ioengine=libaio:走 Linux 异步 IO,能压出高 iodepth。如果编译时没开 libaio,这里会直接报错。direct=1:绕过 page cache。测磁盘真实性能必须开,否则读的是内存,数字虚高。runtime=30+time_based=1:强制跑满 30 秒,而不是把 2G 文件读完就停。这样不同配置之间才有可比性。group_reporting=1:多 job 时汇总输出,避免刷屏。filename+size:指定测试文件和大小。注意size是每个 job 的文件大小,多 job 时会叠加。
运行:
/usr/local/fio-2.2.10/bin/fio seq-read.fio输出里重点看bw(带宽)、iops、lat (msec)的 avg 和 99.00 分位。2.2.10 的百分位输出格式和 3.x 略有不同,99 分位可能标为99.00th,含义一致。
3.2 随机读写与 iodepth 的配合关系
顺序读只能看带宽,真正区分存储介质的是随机读写。把 job 改成随机写:
[rand-write] rw=randwrite bs=4k iodepth=32 numjobs=4这里iodepth=32配合numjobs=4,实际队列深度是 128。2.2.10 在 libaio 引擎下,iodepth是每个 job 的队列深度,总深度是乘积。很多人只改iodepth不改numjobs,发现 IOPS 上不去,就是因为单队列被内核或设备限住了。
跑之前建议先清缓存,避免上一次测试的残留影响:
sync && echo 3 > /proc/sys/vm/drop_caches注意drop_caches需要 root,且只影响 page cache,不影响设备自身缓存。如果设备有写缓存且未开direct,结果依然不可信。
3.3 结果解读:哪些数字能信,哪些是假象
一份 fio 报告里,最容易被误读的是lat和clat。lat是总延迟(包括排队),clat是完成延迟(不含排队)。高 iodepth 下lat远大于clat是正常的,说明瓶颈在队列调度而非设备本身。
另一个坑是bw单位。2.2.10 默认输出KB/s,但这里的 K 是 1024 还是 1000,取决于编译时的定义。稳妥做法是看iops和bs自己算:iops * bs / 1024 / 1024得到 MiB/s,和报告里的bw对照,偏差超过 5% 就要查单位换算。
还有slat(提交延迟),在 libaio 下通常很小,如果slat异常大,说明 CPU 或内核调度有问题,而不是磁盘慢。这时候要看%util和await(用 iostat 配合),交叉验证。
4. 避坑与排查:2.2.10 特有的五个翻车现场
4.1 现象:configure 通过但 make 报 engine 缺失
原因:--enable-libaio虽然写了,但 configure 检测到libaio.h不存在时不会报错,只是静默禁用。解决:configure 后 grep 日志确认:
grep -i libaio configure.log如果输出checking for libaio.h... no,补装libaio-dev后重新 configure,不要直接 make。
4.2 现象:跑起来报fio: pid=xxx, got signal=11
原因:段错误,多半是direct=1配合某些文件系统(如 tmpfs、overlayfs)不支持 O_DIRECT。解决:换到 ext4/xfs 的块设备或普通目录,或者临时去掉direct=1验证。注意去掉 direct 后结果不能用于磁盘选型。
4.3 现象:IOPS 远低于预期,但 iostat 显示设备很闲
原因:numjobs和iodepth乘积不够,或者 job 文件里rw写成了randread但bs太大(如 1M),导致实际变成顺序读。解决:随机测试固定bs=4k或8k,逐步加iodepth直到 IOPS 不再上升,那个拐点才是设备真实能力。
4.4 现象:两次相同配置结果差异超过 20%
原因:没有清缓存,或者后台有其他 IO。解决:每次测试前drop_caches,并用iostat -x 1确认没有其他进程在读写同一设备。另外 2.2.10 的runtime是软限制,实际可能多跑几秒,对比时统一看bw而非总字节数。
4.5 现象:make install后命令找不到
原因:--prefix设到了非 PATH 目录。解决:用绝对路径调用,或临时加 PATH:
export PATH=/usr/local/fio-2.2.10/bin:$PATH不要直接cp到/usr/bin,会和系统包管理器冲突,后期升级或卸载都麻烦。
5. 进阶:用 2.2.10 做可复现的对比测试与结果归档
老版本 fio 最大的价值不是功能多,而是行为稳定——同一份 job 文件在不同机器上跑,结果可比性强。我一般会建一个测试目录,把 job 文件、configure 日志、make 日志、fio 输出全部按时间戳归档:
mkdir -p ~/fio-bench/$(date +%Y%m%d-%H%M%S) cd ~/fio-bench/$(date +%Y%m%d-%H%M%S) cp /path/to/seq-read.fio . /usr/local/fio-2.2.10/bin/fio seq-read.fio --output=seq-read.json --output-format=json--output-format=json在 2.2.10 里已经支持,输出结构化数据,方便后续用 jq 或 python 提取关键指标做对比表:
| 指标 | 字段路径 | 用途 |
|---|---|---|
| 带宽 | jobs[0].read.bw | 顺序读能力 |
| IOPS | jobs[0].write.iops | 随机写能力 |
| 平均延迟 | jobs[0].read.lat_ns.mean | 延迟基线 |
| 99 分位延迟 | jobs[0].read.lat_ns.percentile["99.000000"] | 尾延迟 |
用 jq 提取:
jq '.jobs[0].read.bw, .jobs[0].read.lat_ns.mean' seq-read.json注意 2.2.10 的 JSON 字段名和 3.x 有差异,比如lat_ns在 3.x 里可能叫lat_ns但结构不同,直接套用 3.x 的解析脚本会拿到 null。稳妥做法是先jq 'keys'看顶层结构,再逐层下钻。
另一个技巧是固定 CPU 亲和性,减少调度抖动:
taskset -c 2-5 /usr/local/fio-2.2.10/bin/fio rand-write.fio把 fio 绑到独立核心,避免和其他进程抢 CPU。2.2.10 本身不支持--cpus_allowed参数(3.x 才有),所以用taskset是更可靠的方式。
最后说个血泪经验:老版本 fio 的runtime在time_based=1下,如果某个 job 提前完成(比如文件被写满),整个测试会提前结束,而不是等满 runtime。所以size一定要给足,或者用filesize配合nrfiles让每个 job 有足够空间。我一般会把size设成runtime * 预期带宽 * 1.5,留出余量。希望帮到你。
本文还有配套的精品资源,点击获取