FIO (Flexible I/O Tester) 综合使用手册
版本: v1.0 | 最后更新: 2026-05-27 | 作者: Jens Axboe (FIO), 整理自 CSDN pansaky + RV Script Generator
1. 概述
1.1 什么是 FIO
FIO (Flexible I/O Tester) 是由 Jens Axboe 开发的 Linux 下最强大的磁盘 I/O 性能测试工具。它支持 13 种 I/O 引擎,可通过 Job File 或命令行精确控制测试参数,是服务器硬件验证 (RV Test) 中测量 IOPS、带宽和延迟的标准工具。
1.2 核心能力
- 13 种 I/O 引擎: sync, psync, libaio, mmap, splice, sg, net, cpuio, guasi, rbd, gfapi, fusionsound, rdma - Job File 模式: INI 风格配置文件,支持多 Job 并发 - 丰富的测试模式: 随机/顺序读写、混合读写、TRIM 等 - 精确时延统计: clat (完成延迟), slat (提交延迟), lat (总延迟) - 内置日志与绘图: bandwidth/iops/latency 日志 + fio_generate_plots 自动绘图 - 数据校验: md5, crc32, sha256 等多种校验模式 - 多客户端: --server / --client 模式支持分布式测试
1.3 与其他工具对比
| 工具 | 特点 | 适用场景 |
|---|---|---|
| dd | 简单顺序读写 | 快速单次测试 |
| FIO | 全面可配置,支持多种模式 | 精细化性能验证 |
| IOMeter | Windows 为主,GUI 界面 | Windows 平台 |
| Vdbench | Java 实现,企业级 | 存储系统验收 |
2. 目录结构
2.1 FIO 文件夹结构
FIO/
├── FIO.docx # FIO+IPMI 安装 SOP
├── FIO脚本参数.docx # Job File 格式说明
├── FIO是测试IOPS的非常好的工具.docx # 综合教程 (~650行)
├── README-FIO.md # 本文档
├── fio-2.1.7-1.el6.rf.x86_64.rpm # EL6 RPM 包
├── fio-3.7-2.el7.x86_64.rpm # EL7 RPM 包
├── fio-3.19-3.el8.x86_64.rpm # EL8 RPM 包
├── fio-2.1.7.tar.bz2 # 源码包 v2.1.7
└── fio-3.7.tar.gz # 源码包 v3.7
2.2 RV Script Generator 目录结构
RV Script Generator/
├── RV Script Generator.py # 主程序 (2359 行, Python/Tkinter)
├── RV Script Generator.txt # 说明文档
├── RV Script Generator.exe # PyInstaller 打包的可执行文件
├── IPMItool命令大全.txt # IPMI 风扇控制命令参考
├── image/ # 图标资源
│ ├── RV Script.png
│ └── RV_Script_Generator.ico
├── test/ # FIO 配置模板 (10个)
│ ├── randread-4k-1-16-85%-none
│ ├── randwrite-4k-1-16-85%-none
│ ├── read-128k-1-16-85%-none
│ ├── write-128k-1-16-85%-none
│ ├── read-256k-1-16-85%-none
│ ├── write-256k-1-16-85%-none
│ ├── read-64k-1-16-85%-none
│ ├── write-64k-1-16-85%-none
│ ├── read-1M-1-16-85%-none
│ └── write-1M-1-16-85%-none
├── build/ # PyInstaller 构建产物
└── dist/ # 打包输出
└── RV_Script_Generator.exe
3. 下载路径
3.1 官方源码下载
FIO 官方发布地址为 kernel.dk:
# 下载最新版本 (替换版本号)
wget http://brick.kernel.dk/snaps/fio-3.12.tar.gz
# 历史版本示例
wget http://brick.kernel.dk/snaps/fio-2.2.5.tar.gz
wget http://brick.kernel.dk/snaps/fio-3.7.tar.gz
3.2 Git 仓库
# 官方 Git
git clone git://git.kernel.dk/fio.git
# GitHub 镜像
git clone https://github.com/axboe/fio.git
3.3 YUM / DNF 安装 (RHEL/CentOS/Fedora)
# EPEL 仓库中的 FIO
yum install fio -y # CentOS/RHEL 7
dnf install fio -y # CentOS/RHEL 8+ / Fedora
3.4 APT 安装 (Debian/Ubuntu)
apt-get install fio -y
3.5 本地 RPM 包📁 本地下载
提示:点击按钮将打开网络文件夹,点击即可下载
本目录提供三个版本的 RPM 包,可直接使用:
| 文件 | 适用系统 | FIO 版本 |
|---|---|---|
fio-2.1.7-1.el6.rf.x86_64.rpm |
RHEL/CentOS 6.x (EL6) | 2.1.7 |
fio-3.7-2.el7.x86_64.rpm |
RHEL/CentOS 7.x (EL7) | 3.7 |
fio-3.19-3.el8.x86_64.rpm |
RHEL/CentOS 8.x (EL8) | 3.19 |
3.6 本地源码包
| 文件 | 版本 | 格式 |
|---|---|---|
fio-2.1.7.tar.bz2 |
2.1.7 | bzip2 |
fio-3.7.tar.gz |
3.7 | gzip |
4. 安装说明
4.1 RPM 安装
# 方法一: 直接 rpm 安装
rpm -ivh fio-3.7-2.el7.x86_64.rpm
# 方法二: yum localinstall (自动处理依赖)
yum localinstall fio-3.7-2.el7.x86_64.rpm -y
验证安装:
fio --version
# 输出示例: fio-3.7
4.2 源码编译安装
4.2.1 安装依赖
# CentOS/RHEL
yum install gcc make libaio-devel zlib-devel -y
# Debian/Ubuntu
apt-get install build-essential libaio-dev zlib1g-dev -y
4.2.2 编译步骤
# 解压
tar -zxvf fio-3.7.tar.gz # .tar.gz 格式
# 或
tar -jxvf fio-2.1.7.tar.bz2 # .tar.bz2 格式
# 进入目录
cd fio-3.7
# 配置 (默认选项)
./configure
# 编译
make -j$(nproc)
# 安装
make install
4.2.3 configure 常用选项
./configure --prefix=/usr/local # 指定安装路径
./configure --enable-gfio # 启用 GFIO 图形界面
./configure --disable-native # 禁用本地优化
4.2.4 验证安装
fio --version # 版本号
which fio # 安装路径
fio --help # 帮助信息
4.3 GFIO 图形界面安装
GFIO 是 FIO 的 GTK2 图形前端,适合不熟悉命令行的用户。
4.3.1 安装 GTK2 依赖
# CentOS/RHEL
yum install gtk2 gtk2-devel cairo-devel -y
# Debian/Ubuntu
apt-get install libgtk2.0-dev libcairo2-dev -y
4.3.2 编译带 GFIO 的 FIO
cd fio-3.7
./configure --enable-gfio
make -j$(nproc)
make install
4.3.3 启动 GFIO
gfio &
4.4 测试安装
# 简单随机读测试 (文件模式)
fio --name=test --rw=randread --bs=4k --size=100M --filename=/tmp/fio_test
# 完成后清理
rm /tmp/fio_test
5. 命令行参数
5.1 基本参数
| 参数 | 说明 | 示例 |
|---|---|---|
--name |
Job 名称 | --name=test1 |
--filename |
目标文件或设备 | --filename=/dev/sda |
--rw |
读写模式 | --rw=randread |
--bs |
块大小 | --bs=4k |
--size |
每个 Job 的 I/O 总量 | --size=1G |
--ioengine |
I/O 引擎 | --ioengine=libaio |
5.2 读写模式 (--rw)
| 值 | 说明 |
|---|---|
read |
顺序读 |
write |
顺序写 |
randread |
随机读 |
randwrite |
随机写 |
rw / readwrite |
混合顺序读写 |
randrw |
混合随机读写 |
trim / randtrim |
TRIM (仅 SSD) |
混合读写比例控制:
--rw=randrw --rwmixread=70 # 70% 读, 30% 写
5.3 IO 引擎 (--ioengine)
FIO 支持 13 种 I/O 引擎,适配不同场景:
| 引擎 | 说明 | 特点 |
|---|---|---|
sync |
基本同步 I/O | read/write 系统调用 |
psync |
基本同步 pread/pwrite | positional sync I/O |
libaio |
Linux 原生异步 I/O | 推荐用于磁盘测试 |
mmap |
内存映射 I/O | 文件映射到内存 |
splice |
splice/vmsplice | 零拷贝 I/O |
sg |
SCSI generic | SCSI 设备 |
net |
网络 I/O | 测试网络存储 |
cpuio |
CPU 压力 | 纯 CPU 负载 |
guasi |
GUASI 引擎 | 自定义用户空间异步 |
rbd |
Ceph RADOS 块设备 | Ceph 存储 |
gfapi |
GlusterFS API | Gluster 存储 |
fusionsound |
Fusion-io 设备 | 特定硬件 |
注意事项:
- libaio 在 CentOS 上需安装 libaio-devel - sync 在多 Job 时受限于单线程 - 裸设备测试建议用 libaio + direct=1
5.4 块大小 (--bs)
--bs=4k # 模拟数据库小 IO
--bs=128k # 模拟大文件顺序读写
--bs=1M # 最大吞吐量测试
--bs=4k,8k # 混合块大小
--bsrange=512-4k # 块大小范围 (随机)
5.5 队列深度与并发 (--iodepth, --numjobs)
--iodepth=16 # 队列深度 (单 Job 内并发 IO 数)
--numjobs=4 # 并行 Job 数
关键公式: 总并发 IO 数 = numjobs × iodepth
# 4 个 Job, 每个 iodepth=16 → 总并发 64
fio --name=test --rw=randread --bs=4k --numjobs=4 --iodepth=16 ...
# 单 Job 高队列深度 (模拟数据库)
fio --name=test --rw=randread --bs=4k --numjobs=1 --iodepth=32 ...
5.6 时间控制
| 参数 | 说明 | 示例 |
|---|---|---|
--runtime |
运行时长 (秒) | --runtime=300 |
--ramp_time |
预热时间 (不纳入统计) | --ramp_time=30 |
--time_based |
基于时间运行 (忽略 --size) | 必须与 --runtime 配合 |
# 运行 5 分钟, 前 30 秒预热
fio --name=test --rw=randread --bs=4k --runtime=300 --ramp_time=30 --time_based ...
5.7 数据控制
| 参数 | 说明 | 示例 |
|---|---|---|
--direct |
绕过页缓存 (O_DIRECT) | --direct=1 |
--buffered |
使用页缓存 (默认) | --buffered=1 |
--sync |
同步写入 (O_SYNC) | --sync=1 |
--fsync |
每次写入后 fsync | --fsync=1 |
--norandommap |
不记录随机位置 | --norandommap |
--randrepeat |
随机序列可重复 | --randrepeat=0 |
--refill_buffers |
每次提交后重新填充缓冲区 | --refill_buffers |
--end_fsync |
测试结束后 fsync | --end_fsync=1 |
# 典型裸盘测试配置
--direct=1 --norandommap --randrepeat=0
5.8 校验参数
--verify=md5 # MD5 校验
--verify=crc32c # CRC32C 校验
--verify=sha256 # SHA256 校验
--verify_fatal=1 # 校验失败直接退出
--verify_async=1 # 异步校验 (提高性能)
--verify_backlog=120 # 异步校验队列深度
5.9 速率限制
--rate=100M # 限制带宽为 100MB/s
--rate_iops=5000 # 限制 IOPS 为 5000
5.10 CPU 绑定
--cpus_allowed=0-3 # 绑定到 CPU 0-3
--cpus_allowed_policy=split # 为每个 Job 分配不同 CPU
--numa_cpu_nodes=0 # 绑定 NUMA Node 0
--numa_mem_policy=local # 使用本地内存分配
5.11 日志参数
| 参数 | 说明 |
|---|---|
--write_bw_log |
记录带宽日志 |
--write_iops_log |
记录 IOPS 日志 |
--write_lat_log |
记录延迟日志 |
--log_avg_msec |
日志采样间隔 (毫秒) |
--write_bw_log=result_bw # → result_bw_bw.1.log
--write_iops_log=result_iops # → result_iops_iops.1.log
--write_lat_log=result_lat # → result_lat_lat.1.log
--log_avg_msec=1000 # 每秒采样一次
5.12 输出控制
| 参数 | 说明 |
|---|---|
--output |
输出到文件 |
--output-format |
输出格式 (normal, json, json+, terse) |
--group_reporting |
汇总所有 Job 结果 |
--eta |
显示预计剩余时间 |
--eta-newline |
ETA 进度更新模式 |
5.13 其他常用参数
| 参数 | 说明 |
|---|---|
--thinktime |
每次 IO 间等待 (微秒) |
--thinktime_spin |
自旋等待 (替代 sleep) |
--loops |
循环执行次数 |
--thread |
使用 pthread 替代 fork |
--invalidate |
使能缓存失效 |
--fsync_on_close |
关闭文件前 fsync |
--stonewall / --wait_for_all |
多 Job 等待控制 |
--exitall |
任一 Job 完成后退出所有 |
--create_only=1 |
仅创建文件不测试 |
--create_serialize=0 |
并行创建文件 |
--fadvise_hint |
内核预读提示 (0=random, 1=sequential) |
--random_distribution |
随机分布 (random, zipf, pareto) |
6. Job File 格式
6.1 基本格式
FIO Job File 使用 INI 风格的配置语法,以 [section] 开头:
[global]
; 全局参数,对所有 Job 生效
ioengine=libaio
direct=1
[job1]
; Job 特定参数,可覆盖 global
filename=/dev/sda
rw=randread
6.2 Global 段详解
[global] 段定义的参数会被所有后续 Job 继承,避免重复配置:
[global]
ioengine=libaio # I/O 引擎
direct=1 # O_DIRECT, 绕过缓存
buffered=0 # 不使用缓存
norandommap # 不记录随机 I/O 偏移
randrepeat=0 # 随机种子不可重复
refill_buffers # 每次 IO 后重新填充缓冲区
rw=randread # 读写模式
bs=4k # 块大小
numjobs=1 # 并行 Job 数
iodepth=16 # 队列深度
runtime=120 # 运行时长 (秒)
ramp_time=30 # 预热时间 (秒)
time_based # 基于时间而非数据量
#loops=1 # 循环次数 (注释掉表示不循环)
6.3 Device 段详解
每个磁盘设备作为独立 Job 段,命名规则: [sd{letter}-{test_name}-params]
[sda-randread-4k-1-16-85%-none]
filename=/dev/sda # 目标设备
size=85% # 测试 85% 容量
write_bw_log=/root/rv_test/result/dev/sda/sda-randread-4k-1-16-85%-none-log1
write_iops_log=/root/rv_test/result/dev/sda/sda-randread-4k-1-16-85%-none-log2
write_lat_log=/root/rv_test/result/dev/sda/sda-randread-4k-1-16-85%-none-log3
log_avg_msec=1000 # 日志采样间隔 1s
命名规则解析: 以 sda-randread-4k-1-16-85%-none 为例: - sda: 设备名 (/dev/sda) - randread: 随机读测试 - 4k: 块大小 - 1: numjobs=1 - 16: iodepth=16 - 85%: 测试 85% 容量 - none: 无特殊标记 (非分组)
6.4 完整示例: 多设备配置
#Config File for randread-4k-1-16-85%-none
[global]
ioengine=libaio
direct=1
buffered=0
norandommap
randrepeat=0
refill_buffers
rw=randread
bs=4k
numjobs=1
iodepth=16
runtime=120
ramp_time=30
time_based
[sda-randread-4k-1-16-85%-none]
filename=/dev/sda
size=85%
write_bw_log=/root/rv_test/result/dev/sda/sda-randread-4k-1-16-85%-none-log1
write_iops_log=/root/rv_test/result/dev/sda/sda-randread-4k-1-16-85%-none-log2
write_lat_log=/root/rv_test/result/dev/sda/sda-randread-4k-1-16-85%-none-log3
log_avg_msec=1000
[sdb-randread-4k-1-16-85%-none]
filename=/dev/sdb
size=85%
write_bw_log=/root/rv_test/result/dev/sdb/sdb-randread-4k-1-16-85%-none-log1
write_iops_log=/root/rv_test/result/dev/sdb/sdb-randread-4k-1-16-85%-none-log2
write_lat_log=/root/rv_test/result/dev/sdb/sdb-randread-4k-1-16-85%-none-log3
log_avg_msec=1000
; ... 继续 sdc, sdd, sde ... 直到 sdx
6.5 参数继承规则
[global]参数被所有 Job 继承- Job 段中定义的同名参数覆盖 global
- 未在 Job 段中定义的参数取 global 值
filename必须每个 Job 单独指定
7. 测试类型
7.1 4K 随机读写
最常见的小块随机 IO 测试,衡量存储设备的 IOPS 性能。
rw=randread # 或 randwrite
bs=4k
iodepth=16 # 或 32
用途: 数据库 OLTP 场景模拟、SSD 最大 IOPS 测试
命令行快捷方式:
# 4K 随机读
fio --name=4k-randread --filename=/dev/sda --rw=randread --bs=4k --ioengine=libaio --direct=1 --iodepth=16 --numjobs=1 --runtime=120 --time_based
# 4K 随机写
fio --name=4k-randwrite --filename=/dev/sda --rw=randwrite --bs=4k --ioengine=libaio --direct=1 --iodepth=16 --numjobs=1 --runtime=120 --time_based
7.2 128K 顺序读写
大块顺序 IO,衡量最大吞吐量。
rw=read # 或 write
bs=128k
iodepth=16
用途: 大文件传输、视频流、备份场景
7.3 256K 顺序读写
rw=read # 或 write
bs=256k
iodepth=16
7.4 64K 顺序读写
rw=read # 或 write
bs=64k
iodepth=16
7.5 1M 顺序读写
最大块大小测试,追求峰值带宽。
rw=read # 或 write
bs=1M
iodepth=16
7.6 混合读写
rw=randrw
rwmixread=70 # 70% 读, 30% 写
bs=4k
iodepth=16
7.7 RV Script Generator 测试类型预设
工具内置 10 种测试类型模板:
| 分组 | 测试名称 | rw | bs | tc | iodepth | runtime |
|---|---|---|---|---|---|---|
| 4K Random | randread-4k | randread | 4k | iops | 16 | 120 |
| 4K Random | randwrite-4k | randwrite | 4k | bw | 16 | 120 |
| 128K Sequential | read-128k | read | 128k | iops | 16 | 120 |
| 128K Sequential | write-128k | write | 128k | bw | 16 | 120 |
| 256K Sequential | read-256k | read | 256k | iops | 16 | 120 |
| 256K Sequential | write-256k | write | 256k | bw | 16 | 120 |
| 64K Sequential | read-64k | read | 64k | iops | 16 | 120 |
| 64K Sequential | write-64k | write | 64k | bw | 16 | 120 |
| 1M Sequential | read-1M | read | 1M | iops | 16 | 120 |
| 1M Sequential | write-1M | write | 1M | bw | 16 | 120 |
其中 tc 字段: iops 表示结果提取 IOPS 值, bw 表示结果提取带宽值。
8. 结果分析与可视化
8.1 实时输出解读
FIO 运行结束后的标准输出示例:
test: (g=0): rw=randread, bs=4K-4K/4K-4K, ioengine=libaio, iodepth=16
fio-3.7
Starting 1 process
Jobs: 1 (f=1): [r] [100.0% done] [525.6MB/s] [134k] [r=0,w=0]
...
Run status group 0 (all jobs):
READ: bw=528MiB/s (553MB/s), 528MiB/s-528MiB/s (553MB/s-553MB/s), io=15.5GiB (16.6GB), run=30045-30045msec
关键指标: - bw: 带宽 (MiB/s, MB/s) - IOPS: 后面显示的 [134k] 表示每秒 134,000 IO - io: 总传输量 - run: 实际运行时间 (ms)
详细时延统计:
clat (usec): min=13, max=1234, avg=45.21, stdev=12.34
lat (usec): min=14, max=1240, avg=46.10, stdev=12.50
- clat: 完成延迟 (Completion Latency) — 提交到完成的时间 - slat: 提交延迟 (Submission Latency) — 发起请求到提交的时间 - lat: 总延迟 (Total Latency) = slat + clat
8.2 日志文件格式
FIO 生成的三类日志文件均为 CSV 格式:
Bandwidth Log (bw_log)
timestamp_ms, bandwidth_bytes, direction
1000, 268435456, 0
2000, 271581184, 0
...
IOPS Log (iops_log)
timestamp_ms, iops, direction
1000, 65536, 0
2000, 66304, 0
...
Latency Log (lat_log)
timestamp_ms, latency_usec, direction, data_size
1000, 45, 0, 4096
2000, 42, 0, 4096
...
8.3 fio_generate_plots
FIO 自带的绘图脚本,需要 gnuplot:
# 1. 运行 FIO 生成日志
fio --name=test --rw=randread --bs=4k --size=1G \
--write_bw_log=result --write_iops_log=result --write_lat_log=result \
--log_avg_msec=1000
# 2. 使用 fio_generate_plots 绘图
fio_generate_plots result /path/to/output/dir
# 输出文件:
# result_bw.svg
# result_iops.svg
# result_clat.svg
# result_slat.svg
# result_lat.svg
8.4 fio2gnuplot
更高级的 gnuplot 封装,支持多种图表类型:
# 安装 (Python 依赖)
pip install fio2gnuplot
# 生成 gnuplot 脚本
fio2gnuplot -p result_bw_log -g
# 绘图
gnuplot result.pg
8.5 手动 gnuplot 绘图
# bandwidth.gnuplot
set terminal png size 1200,600
set output 'bandwidth.png'
set title 'FIO Bandwidth Over Time'
set xlabel 'Time (seconds)'
set ylabel 'Bandwidth (MB/s)'
plot 'result_bw.1.log' using ($1/1000):($2/1024/1024) with lines title 'Read BW'
gnuplot bandwidth.gnuplot
8.6 IOPS 性能分析
磁盘 IOPS 理论值
| 磁盘类型 | 转速 | IOPS (理论) |
|---|---|---|
| SATA HDD | 7200 RPM | ~75-100 |
| SAS HDD | 10K RPM | ~100-140 |
| SAS HDD | 15K RPM | ~150-200 |
| SATA SSD | N/A | 5,000-90,000 |
| NVMe SSD | N/A | 100,000-1,000,000+ |
RAID 写惩罚
| RAID 级别 | 读 IOPS 公式 | 写 IOPS 公式 | 写惩罚 |
|---|---|---|---|
| RAID 0 | N × IOPS | N × IOPS | 1x |
| RAID 1 | N × IOPS | (N/2) × IOPS | 2x |
| RAID 5 | N × IOPS | (N/4) × IOPS | 4x |
| RAID 6 | N × IOPS | (N/6) × IOPS | 6x |
| RAID 10 | N × IOPS | (N/2) × IOPS | 2x |
其中 N = 磁盘数量, IOPS = 单盘 IOPS
示例计算: - 6 块 15K SAS 盘 (150 IOPS/盘) 做 RAID5 - 读 IOPS: 6 × 150 = 900 - 写 IOPS: 6 × 150 / 4 = 225
9. RV Script Generator 自动化工具
9.1 工具概述
RV Script Generator 是一个基于 Python/Tkinter 的图形化工具,为服务器 RV (Reliability Verification) 测试自动化生成 FIO 配置文件和 Shell 测试脚本。它集成了 IPMI 风扇控制,支持按风扇转速百分比逐级测试磁盘 I/O 性能。
技术栈: Python 3 + Tkinter (GUI) + PyInstaller (打包为 .exe)
位置: e:\Auto Test\测试脚本\FIO自动化测试脚本\RV Script Generator\
9.2 使用流程 (7 步向导)
Step 1: New Project (新建项目)
- 输入项目名称 (只能包含字母、数字、下划线) - 选择输出目录 (默认为工具所在目录) - 预览: 显示将创建的完整路径
Step 2: Select Test Types (选择测试类型)
界面分为 5 个分组:
| 分组 | 包含测试 |
|---|---|
| 4K Random | randread-4k, randwrite-4k |
| 128K Sequential | read-128k, write-128k |
| 256K Sequential | read-256k, write-256k |
| 64K Sequential | read-64k, write-64k |
| 1M Sequential | read-1M, write-1M |
每个测试可单独调整: - iod (iodepth): 16 或 32 - run(s) (runtime): 120, 300, 600, 900 秒 - ramp(s) (ramp_time): 30 或 120 秒 - logms (log_avg_msec): 1000 或 12000
Custom Test Type (自定义): 可定义任意 rw/bs/tc/iodepth/runtime 组合。
FIO Settings 全局设置: - iodepth/runtime/ramp_time/log_avg_msec: 启用 "Use Global" 时覆盖所有测试的对应值 - FIO Output Case: lowercase (iops=/bw=) vs UPPERCASE (IOPS=/BW=) - 提示: Tencent 和 Yunhai 平台通常使用 UPPERCASE - Test Mode: - batch (all drives together): 所有盘在一个 FIO 命令中测试 - per-drive (one by one): 每个盘单独运行 FIO (Tencent/Steelix 常用)
Step 3: Drive Select (选择磁盘)
- 输入盘符范围,如 a-z (默认) 或 a-f - 默认覆盖 24 个盘符 (a 到 x) - 支持多字母盘符: aa-az, ba-bz, ca-cz, da-dz (共 130 个标签)
Step 4: Fan Speed (风扇转速百分比)
预设转速序列: - Standard: 20 30 40 50 60 70 75 80 85 90 95 100 (12级) - Classic: 20 30 40 50 60 70 80 90 100 (9级) - Single 100: 仅 100% - Single 20: 仅 20%
也可手动输入自定义序列 (空格分隔的百分比值)。
Step 5: Fan Control (IPMI 风扇控制平台)
10 个平台预设:
| 平台 | 控制命令 | 风扇数 | 恢复方式 |
|---|---|---|---|
| Current IPT (0x3e 0x1f) | raw 0x3e 0x1f $j $fan $fan 0 0xff 0xff |
10 | 循环恢复 21% |
| Seaking/Machamp (0x30 0xf6) | raw 0x30 0xf6 0x0$j $fan $fan 0x03 |
6 | 循环恢复 20-100 |
| Pokemon (0x30 0xb7) | raw 0x30 0xb7 0x4 0xff $fan + 0xb7 0x5 0xff $fan |
1 | IPMI stack restart |
| Marshadow (0x38 0x41) | raw 0x38 0x41 0 $fan 0xff |
1 | 恢复自动模式 |
| Kingler (0x2e 0x11) | raw 0x2e 0x11 0xa9 0x19 0x00 6 1 + 0x2e 0x11 ... 7 $fan |
1 | mc reset cold |
| Steelix (0x3e 0xf6) | raw 0x3e 0xf6 $j $fan $fan 0x03 |
5 | 循环恢复 30% |
| Yunhai (0x3e 0x31) | raw 0x3e 0x31 0x00 $fan (前导: 0x3e 0x2f 0x01) |
1 | 恢复 50% |
| U90G5/Poyanghu (0x3e 0x03) | raw 0x3e 0x03 0x0$j $fan |
8 | 循环恢复 50% |
| Kunlun/2U42 (0x3e 0x03) | raw 0x3e 0x03 $j $fan |
8 | 循环恢复 50% |
| Old IPT (0x30 0xf6 plain) | raw 0x30 0xf6 $j $fan $fan 0 |
5 | 循环恢复 30-100 |
| Auto Only (no IPMI) | 无 | — | — |
支持用户编辑自定义的 set_fan 和 restore 命令。
Step 6: Summary (预览)
展示所有选定参数的综合摘要,确认后进入生成步骤。
Step 7: Generate (生成)
点击生成后,在 {output_dir}/{project_name}/ 下创建:
project_name/
├── test/ # FIO 配置文件
│ ├── randread-4k-1-16-85%-none
│ ├── read-128k-1-16-85%-none
│ └── ... (每种选定的测试类型)
├── *.sh # Shell 测试脚本 (每种测试类型一个)
└── result/ # (运行后生成) 测试结果
9.3 生成的 Shell 脚本结构
每个 .sh 脚本的工作流程:
1. 初始化: 获取脚本路径、时间戳、创建结果目录
2. SMART 采集 (before): 遍历所有盘执行 smartctl --all
3. 测试循环:
├─ 外循环: test_case (测试类型)
│ ├─ 外循环 (per-drive模式): $hdd 盘符
│ │ ├─ 内循环: fan 转速
│ │ │ ├─ IPMI 设置风扇转速 (raw 命令)
│ │ │ ├─ sleep 等待 (5-10秒)
│ │ │ ├─ 执行 fio {config_file}
│ │ │ └─ 提取结果 (grep IOPS/BW → CSV)
│ │ └─ 内循环结束
│ └─ 外循环结束
└─ 后处理: paste 合并各盘结果 CSV
4. SMART 采集 (after)
5. 恢复风扇到自动模式
6. 重命名结果目录 (带时间戳)
核心代码段示例 (batch 模式):
#!/bin/sh
Cur_Dir=$(cd "$(dirname "$0")";pwd)
ScriptName=$(basename "$0" .sh)
TimeStamp=$(date +%Y%m%d%H%M)_${ScriptName}
rm -rf $Cur_Dir/$ScriptName/result
mkdir -p $Cur_Dir/$ScriptName/result/smart
mkdir -p $Cur_Dir/$ScriptName/result/value
mkdir -p $Cur_Dir/$ScriptName/result/log
## Smart Log Collect
hdd='a b c d e f g h i j k l m n o p q r s t u v w x'
for i in $hdd;do
mkdir -p $Cur_Dir/$ScriptName/result/dev/sd$i
smartctl --all /dev/sd$i >$Cur_Dir/$ScriptName/result/smart/smart_info_sd${i}_before
done
## Set test_case
for test_case in randread-4k;do
for fan in 20 30 40 50 60 70 75 80 85 90 95 100;do
# IPMI 设置风扇
ipmitool raw 0x3e 0x1f 1 $fan $fan 0 0xff 0xff
sleep 5
# FIO 测试
fio $Cur_Dir/test/${test_case}-1-16-85%-none |tee $Cur_Dir/$ScriptName/result/log/${test_case}-${fan}
# 提取结果
cd $Cur_Dir/$ScriptName/result/log
tc=iops
grep -i -o "iops=[0-9]\+," ${test_case}-${fan} |tee ycy.txt
grep -o "[0-9]\+" ycy.txt |tee $Cur_Dir/$ScriptName/result/value/$fan-${test_case}-value-$tc.csv
done
done
## 恢复风扇
for j in `seq 1 10`;do
ipmitool raw 0x3e 0x1f $j 21 100 0 0xff 0xff
done
## Smart Log Collect (after)
for i in $hdd;do
smartctl --all /dev/sd$i >$Cur_Dir/$ScriptName/result/smart/smart_info_sd${i}_after
done
mv $Cur_Dir/$ScriptName/result $Cur_Dir/$TimeStamp
9.4 两种测试模式对比
Batch 模式 (all drives together)
- 所有盘在一个 FIO Job File 中配置 - 一次 fio config_file 调用测试所有盘 - 适合: 批量快速测试,Intel/Seaking 平台
循环: fan 转速
└─ fio config_file (含 sda, sdb, sdc ... sdx 全部段)
└─ 提取结果
Per-Drive 模式 (one by one)
- 每个盘独立 FIO 配置和运行 - 嵌套循环: 盘 → 转速 → FIO - 适合: Tencent/Steelix 平台,需要每个盘的独立结果
循环: $hdd 盘符
└─ 循环: fan 转速
└─ fio config_file (仅含当前 sd$i 段)
└─ 提取结果
9.5 输出格式: UPPERCASE vs lowercase
lowercase (默认):
tc=iops
grep -i -o "iops=[0-9]\+\," ${test_case}-${fan} |tee ycy.txt
grep -o "[0-9]\+" ycy.txt |tee $Cur_Dir/$ScriptName/result/value/$fan-${test_case}-value-$tc.csv
UPPERCASE:
tc=iops
grep -o "IOPS=[0-9]\+," ${test_case}-${fan} |tee ycy.txt
grep -o "[0-9]\+" ycy.txt |tee $Cur_Dir/$ScriptName/result/value/$fan-${test_case}-value-$tc.csv
区别: FIO 的不同版本的输出格式可能不同,UPPERCASE 匹配IOPS=模式,lowercase 匹配iops=模式。Tencent 和 Yunhai 平台通常使用 UPPERCASE。
9.6 结果提取逻辑
IOPS 提取 (tc=iops)
FIO Output: "... iops=134567, ..."
→ grep "iops=[0-9]+" → ycy.txt
→ grep numbers → value/{fan}-{test_case}-value-iops.csv
BW 提取 (tc=bw)
FIO Output: "... bw=528MiB/s ..."
→ grep "bw=[0-9]+KiB" → ycy
→ 单位转换 (KiB→MiB: ÷1024) → value/{fan}-{test_case}-value-bw.csv
lowercase 模式下 KiB/KB 自动除以 1024 转换为 MiB; 如果已经是 MiB 则直接输出。
9.7 后处理
# 合并所有风扇转速的结果
cd $Cur_Dir/$ScriptName/result/value
paste -d ',' `ls |grep ${test_case}-value-$tc.csv|xargs` \
|tee $Cur_Dir/$ScriptName/result/${test_case}-value-$tc.csv
# 合并所有盘的结果
cd $Cur_Dir/$ScriptName/result/dev/sd$i
fio_iops_file=`ls |grep sd$i |grep $test_case|grep $tc`
awk -F ',' '{print $1","$2}' $fio_iops_file \
|tee $Cur_Dir/$ScriptName/result/${test_case}/sd${i}_${test_case}
# 最终合并
cd $Cur_Dir/$ScriptName/result/${test_case}
paste -d ',' `ls |grep "$test_case"|grep sd|xargs` \
|tee $Cur_Dir/$ScriptName/result/${test_case}-log-$tc.csv
最终生成的 CSV: - 行: 日志采样时间点 - 列: 各盘的 IOPS/BW 值
10. 使用示例
10.1 单盘随机读测试 (命令行)
fio --name=randread-test \
--filename=/dev/sda \
--rw=randread \
--bs=4k \
--ioengine=libaio \
--direct=1 \
--iodepth=16 \
--numjobs=1 \
--runtime=120 \
--ramp_time=30 \
--time_based
10.2 多盘并发测试 (Job File)
创建 multi_disk.fio:
[global]
ioengine=libaio
direct=1
rw=randread
bs=4k
iodepth=16
runtime=120
ramp_time=30
time_based
[sda]
filename=/dev/sda
size=85%
[sdb]
filename=/dev/sdb
size=85%
[sdc]
filename=/dev/sdc
size=85%
运行:
fio multi_disk.fio
10.3 全盘顺序写测试
fio --name=seq-write \
--filename=/dev/sda \
--rw=write \
--bs=128k \
--ioengine=libaio \
--direct=1 \
--iodepth=16 \
--numjobs=1 \
--size=100%
警告: --rw=write 会覆盖磁盘数据!确保测试盘无重要数据。
10.4 混合读写测试
fio --name=mixed-rw \
--filename=/dev/sda \
--rw=randrw \
--rwmixread=70 \
--bs=4k \
--ioengine=libaio \
--direct=1 \
--iodepth=16 \
--runtime=300 \
--time_based
10.5 延迟测试
使用不同的 iodepth 观察延迟变化:
# 低队列深度 (低延迟场景)
fio --name=lat-qd1 --filename=/dev/sda --rw=randread --bs=4k \
--ioengine=libaio --direct=1 --iodepth=1 --runtime=60 --time_based
# 高队列深度 (高并发场景)
fio --name=lat-qd32 --filename=/dev/sda --rw=randread --bs=4k \
--ioengine=libaio --direct=1 --iodepth=32 --runtime=60 --time_based
关键对比指标: clat avg, clat p99, clat p99.9
10.6 数据完整性校验
fio --name=verify-test \
--filename=/dev/sda \
--rw=randwrite \
--bs=4k \
--size=1G \
--ioengine=libaio \
--direct=1 \
--verify=md5 \
--verify_fatal=1
# 验证阶段 (重新读取并校验)
fio --name=verify-test \
--filename=/dev/sda \
--rw=randread \
--bs=4k \
--size=1G \
--ioengine=libaio \
--direct=1 \
--verify=md5 \
--verify_only
10.7 日志记录与绘图完整流程
# 1. 准备结果目录
PROJECT_DIR="/root/rv_test"
mkdir -p $PROJECT_DIR/result
# 2. 运行 FIO 并记录日志
fio --name=test \
--filename=/dev/sda \
--rw=randread \
--bs=4k \
--ioengine=libaio \
--direct=1 \
--iodepth=16 \
--runtime=300 \
--ramp_time=30 \
--time_based \
--write_bw_log=$PROJECT_DIR/result/sda_bw \
--write_iops_log=$PROJECT_DIR/result/sda_iops \
--write_lat_log=$PROJECT_DIR/result/sda_lat \
--log_avg_msec=1000 \
--output=$PROJECT_DIR/result/fio_output.txt
# 3. 使用 fio_generate_plots 绘图
fio_generate_plots sda $PROJECT_DIR/result/charts
# 4. 查看图表
ls $PROJECT_DIR/result/charts/
# sda_bw.svg, sda_iops.svg, sda_clat.svg, sda_slat.svg, sda_lat.svg
10.8 服务器批量测试 (RV Script Generator 工作流)
1. 打开 RV Script Generator.py (或 .exe)
2. New Project → 输入 "rv_myproject"
3. Select Test Types → 勾选全部 10 种预设
4. Drive Select → 输入 "a-x"
5. Fan Speed → 选择 "Standard" (12级)
6. Fan Control → 选择 "Current IPT (0x3e 0x1f)"
7. Summary → 确认参数无误
8. Generate → 生成脚本和配置文件
输出:
rv_myproject/
├── test/ (10 个 FIO 配置文件)
├── randread-4k.sh (每种测试 1 个 .sh)
├── randwrite-4k.sh
├── ...
└── write-1M.sh
运行:
cd rv_myproject
chmod +x *.sh
./randread-4k.sh
./randwrite-4k.sh
...
11. 高级用法
11.1 IO 引擎深度对比
| 场景 | 推荐引擎 | 原因 |
|---|---|---|
| 本地磁盘/SDD | libaio | 异步 IO, 支持高队列深度 |
| 简单测试 | sync | 无需额外依赖 |
| 文件系统测试 | mmap | 模拟内存映射行为 |
| 网络存储 | net | RAW socket IO |
| Ceph RBD | rbd | 直接访问 RADOS 块 |
| SCSI 设备 | sg | SCSI 通用接口 |
11.2 NUMA 绑定
多路服务器上绑定 NUMA 节点以提高测试稳定性:
fio --name=numa-test \
--filename=/dev/nvme0n1 \
--rw=randread --bs=4k \
--ioengine=libaio --direct=1 \
--iodepth=32 --numjobs=4 \
--cpus_allowed=0-7 \
--cpus_allowed_policy=split \
--numa_cpu_nodes=0 \
--numa_mem_policy=local \
--runtime=300 --time_based
11.3 多客户端模式 (Client/Server)
多台机器协同测试:
# Server 端 (每台被测机器)
fio --server --daemonize=/tmp/fio.pid
# Client 端 (控制机)
fio --client=10.0.0.1 --client=10.0.0.2 job.fio
11.4 IO 日志回放
捕获和重放真实 IO 工作负载:
# 记录 IO trace
fio --name=record \
--filename=/dev/sda \
--rw=randread --bs=4k \
--write_iolog=/tmp/replay.log \
--runtime=300 --time_based
# 重放 IO trace
fio --name=replay \
--filename=/dev/sdb \
--read_iolog=/tmp/replay.log
11.5 硬盘预处理 (Pre-conditioning)
SSD 测试前进行全盘顺序写确保稳态:
# 第一次全盘写 (预处理)
fio --name=precondition \
--filename=/dev/sda \
--rw=write --bs=128k \
--ioengine=libaio --direct=1 \
--iodepth=16 --numjobs=1 \
--size=100%
# 第二次全盘写 (确认稳态)
fio --name=precondition2 \
--filename=/dev/sda \
--rw=write --bs=128k \
--ioengine=libaio --direct=1 \
--iodepth=16 --numjobs=1 \
--size=100%
# 正式测试
fio --name=steady-test \
--filename=/dev/sda \
--rw=randwrite --bs=4k \
--ioengine=libaio --direct=1 \
--iodepth=16 --runtime=1800 --time_based
11.6 随机分布算法
--random_distribution=zipf:1.2 # Zipf 分布 (热点数据)
--random_distribution=pareto:0.9 # Pareto 分布 (80/20)
--random_distribution=random # 均匀随机 (默认)
12. 故障排查
12.1 libaio 缺失
症状: 编译时报 fio: engine libaio not loadable
# CentOS/RHEL
yum install libaio-devel -y
# 已编译安装的需重新编译
cd fio-3.7
make clean
./configure && make && make install
12.2 设备重叠
症状: fio: job 'xxx' dropped because of overlapping
原因: 多个 Job 指定了相同的 filename。
解决: 确保每个 Job 的 filename 唯一,或使用不同的起始 offset:
[sda_part1]
filename=/dev/sda
offset=0
size=50G
[sda_part2]
filename=/dev/sda
offset=50G
size=50G
12.3 权限问题
症状: fio: looks like your file doesn't exist, or you don't have permission
解决: 以 root 运行或确保对设备文件有读写权限:
# 检查权限
ls -la /dev/sda
# 以 root 运行
sudo fio ...
# 或调整用户组
sudo usermod -a -G disk $USER
12.4 日志路径不存在
症状: 日志目录无法写入
解决: 确保日志路径中的目录已创建:
mkdir -p /root/rv_test/result/dev/sda
12.5 大容量设备 size 计算
size=85% 表示使用设备 85% 的容量。对于大容量设备 (如 16TB),测试时间会非常长。
建议: - 随机测试: 使用 runtime=300 + time_based 代替 size - 顺序测试: 指定具体大小 size=500G 或使用 runtime
12.6 GFIO 启动失败
症状: gfio: error while loading shared libraries: libgtk-x11-2.0.so
# CentOS/RHEL
yum install gtk2 gtk2-devel -y
# 检查库路径
ldconfig -p | grep gtk
# 如仍失败,设置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/lib64:$LD_LIBRARY_PATH
gfio
12.7 编译错误: zlib
症状: configure: error: zlib not found
yum install zlib-devel -y # CentOS/RHEL
apt-get install zlib1g-dev # Debian/Ubuntu
13. 相关文档
14. 版本历史
| 版本 | 日期 | 变更说明 |
|---|---|---|
| v1.0 | 2026-05-27 | 初始版本, 基于 CSDN pansaky 博客 + RV Script Generator 源码整理 |
参考来源: CSDN pansaky 博客 (https://blog.csdn.net/pansaky/article/details/83689110), FIO 官方文档, RV Script Generator v1.0 源码