使用fio测试EMMC
1 顺序写压力测试
1.1 命令
- 2GB + 30min 循环写
fio --name=emmc_seq_write_stress \
--filename=fio_test_file.tmp \
--size=2G \
--rw=write \
--bs=1M \
--direct=1 \
--ioengine=libaio \
--iodepth=32 \
--numjobs=1 \
--runtime=1800 \
--time_based \
--group_reportin
| 参数 | 含义 | 对 eMMC 的影响 |
|---|---|---|
--name=emmc_seq_write_stress |
任务名称 | 仅用于报告标识 |
--filename=fio_test_file.tmp |
测试文件 | 直接在文件系统上写入 |
--size=2G |
单个文件大小 | 小于 eMMC 容量,避免写爆 |
--rw=write |
顺序写(非 randwrite) | 模拟大文件写入、固件升级等场景 |
--bs=1M |
块大小 1MB | 大块 IO,充分发挥 eMMC 顺序写带宽 |
--direct=1 |
绕过 page cache | 测的是真实 eMMC 性能,不受内存缓存影响 |
--ioengine=libaio |
Linux 异步 IO | 适合高 iodepth 场景 |
--iodepth=32 |
队列深度 32 | 给 eMMC 足够多的请求,压满内部并行度 |
--numjobs=1 |
单线程 | 避免多线程干扰,简化分析 |
--runtime=1800 |
最长运行 1800 秒 | 长时压力测试 |
--time_based |
时间驱动而非 size 驱动 | 即使写完 2G 也会循环写 |
--group_reporting |
合并 job 统计 | 单 job 时影响不大 |
这里为你补充了面向测试人员的详细解读话术。你可以直接将这些内容放入你的测试文档或指导手册中,说明不仅要知其然,还要让测试人员知道“看这个数据是为了排查什么问题”。
1.2 结果解读
emmc_seq_write_stress: (groupid=0, jobs=1): err= 0: pid=2269: Tue Jul 7 11:11:52 2026
write: IOPS=215, BW=215MiB/s (226MB/s)(126GiB/600324msec); 0 zone resets
slat (usec): min=71, max=153063, avg=369.42, stdev=3715.59
clat (msec): min=5, max=3984, avg=148.34, stdev=55.33
lat (msec): min=5, max=3984, avg=148.71, stdev=55.21
clat percentiles (msec):
| 1.00th=[ 70], 5.00th=[ 140], 10.00th=[ 140], 20.00th=[ 140],
| 30.00th=[ 142], 40.00th=[ 142], 50.00th=[ 144], 60.00th=[ 146],
| 70.00th=[ 148], 80.00th=[ 150], 90.00th=[ 153], 95.00th=[ 161],
| 99.00th=[ 249], 99.50th=[ 279], 99.90th=[ 1099], 99.95th=[ 1234],
| 99.99th=[ 1502]
bw ( KiB/s): min=186368, max=266240, per=99.98%, avg=220283.16, stdev=2829.18, samples=1200
iops : min= 182, max= 260, avg=215.06, stdev= 2.77, samples=1200
lat (msec) : 10=0.05%, 20=0.17%, 50=0.45%, 100=0.81%, 250=97.55%
lat (msec) : 500=0.62%, 750=0.06%, 1000=0.13%, 2000=0.14%, >=2000=0.01%
cpu : usr=2.43%, sys=3.62%, ctx=268408, majf=0, minf=14
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=100.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
issued rwts: total=0,129169,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=32
Run status group 0 (all jobs):
WRITE: bw=215MiB/s (226MB/s), 215MiB/s-215MiB/s (226MB/s-226MB/s), io=126GiB (135GB), run=600324-600324msec
Disk stats (read/write):
mmcblk0: ios=0/258658, merge=0/746, ticks=0/37350755, in_queue=37351082, util=100.00%
1.2.1 BW(带宽)+ IOPS:核心性能指标
BW=215MiB/s (226MB/s), IOPS=215
- 怎么看:这是本次顺序写测试的最终成绩单。
BW代表吞吐量,即每秒写入了多少兆数据;IOPS代表每秒处理的 I/O 请求数。因为我们设置了bs=1M(1MB的数据块),所以 215 次请求正好对应 215MB/s。 - 测试判定:测试人员需要将此处的
BW数值与 eMMC 芯片规格书(Spec)或立项标准进行对比。如果达标且长时间压测未出现断崖式掉速,则该项 Pass。
1.2.2 clat 长尾(99% / 99.9%):系统的抗卡顿能力
99.00th=[249], 99.90th=[1099]
- 怎么看:
clat(Completion Latency) 是指令下发到硬盘完成写入的真实硬件耗时。不要只看平均值(Average),要看分位图(Percentiles)。99.00th=[249]意味着 99% 的写入都在 249 毫秒内完成了,但99.90th=[1099]意味着有千分之一的极端情况,写入卡顿了超过 1 秒。 - 测试判定:这种偶尔的秒级延迟通常是 eMMC 内部主控在做垃圾回收(GC)或磨损均衡。测试人员需关注这个“长尾延迟”是否过大(例如飙升到数秒),过大的毛刺会导致上层应用(如视频录制、OTA升级)出现超时甚至系统无响应。
1.2.3 util(设备利用率):确认测试是否有效
util=100.00%
- 怎么看:这是块设备(
mmcblk0)在压测期间的繁忙程度。在做极限压力测试时,这个值必须 ≈ 100%。这意味着系统的性能瓶颈成功卡在了 eMMC 硬件上。 - 测试判定:如果看到
util只有 50% 甚至更低,说明测试作废,数据不具备参考价值。可能的原因包括: iodepth(队列深度)给得太低,没把磁盘喂饱。- 漏加了
--direct=1,导致数据全写在了操作系统的内存缓存里,根本没真实落盘。 - CPU 性能太弱或者驱动有 Bug,导致发包速度跟不上 eMMC 的写入速度。
1.2.4 IO depth 分布:验证异步 I/O 是否生效
32=100.0%
- 怎么看:这个指标用于自证测试参数是否被系统真实执行。我们命令里要求
--iodepth=32,这里显示 100% 的 I/O 确实是以 32 的并发深度保持下发的。 - 测试判定:用来排查测试工具或系统配置异常。如果参数传了 32,但这里显示
1=99.0%, 32=1.0%,说明系统底层的异步机制(libaio)没生效(比如文件系统不支持或引擎配置错误),I/O 被强制退化成了单队列串行写入,测出来的性能不是硬件的真实极限。
2 4K 随机混合读写测试
2.1 命令
fio --name=emmc_rand_rw_stress \
--filename=fio_test_file.tmp \
--size=2G \
--rw=randrw \
--rwmixread=70 \
--bs=4k \
--direct=1 \
--ioengine=libaio \
--iodepth=32 \
--numjobs=4 \
--runtime=1800 \
--time_based \
--group_reporting
| 参数 | 含义 | 对 eMMC 的影响 |
|---|---|---|
--name=emmc_rand_rw_stress |
任务名称 | 仅用于报告标识,区分测试类型 |
--filename=fio_test_file.tmp |
测试文件 | 直接在文件系统上读写,引入文件系统元数据开销 |
--size=2G |
单个 Job 文件大小 | 4 个 Job 若共享文件则总范围 2G;若独立则为 8G,需留意空间占用 |
--rw=randrw |
随机混合读写 | 模拟真实业务负载,极度考验 eMMC 主控 FTL 的映射表管理 |
--rwmixread=70 |
读写比例 70% 读 / 30% 写 | 贴近 Android/Linux 日常使用场景(读多写少) |
--bs=4k |
块大小 4KB | 极小的 IO 单元,旨在打满 IOPS,暴露 eMMC 随机读写性能瓶颈 |
--direct=1 |
绕过 page cache | 测的是真实 eMMC 性能,排除内存缓存干扰 |
--ioengine=libaio |
Linux 异步 IO | 允许单进程发送大量 IO 请求,匹配高 iodepth |
--iodepth=32 |
单 Job 队列深度 32 | 保持大量请求挂起,迫使 eMMC 内部进行复杂的调度与合并 |
--numjobs=4 |
4 个并发进程 | 模拟多 App 或多线程并发访问,加剧文件系统锁和 eMMC 通道竞争 |
--runtime=1800 |
最长运行 1800 秒 | 长时压力测试,足以触发 eMMC 后台 GC(垃圾回收) |
--time_based |
时间驱动而非 size 驱动 | 确保测试时长恒定,便于观察性能抖动和稳态表现 |
--group_reporting |
合并 Job 统计 | 将 4 个进程的数据汇总,便于查看整体吞吐量,但会掩盖单进程异常 |
2.2 结果解读
emmc_rand_rw_stress: (groupid=0, jobs=4): err= 0: pid=2400: Tue Jul 7 11:50:24 2026
read: IOPS=2097, BW=8391KiB/s (8592kB/s)(2966MiB/362022msec)
slat (usec): min=16, max=494942, avg=91.32, stdev=1419.64
clat (usec): min=116, max=4192.1k, avg=27116.04, stdev=27511.51
lat (usec): min=1025, max=4192.2k, avg=27208.79, stdev=27543.39
clat percentiles (usec):
| 1.00th=[ 1680], 5.00th=[ 3228], 10.00th=[ 5211], 20.00th=[ 9241],
| 30.00th=[ 13304], 40.00th=[ 17695], 50.00th=[ 22414], 60.00th=[ 27395],
| 70.00th=[ 33424], 80.00th=[ 40633], 90.00th=[ 51119], 95.00th=[ 61604],
| 99.00th=[108528], 99.50th=[147850], 99.90th=[287310], 99.95th=[425722],
| 99.99th=[675283]
bw ( KiB/s): min= 136, max=12322, per=99.99%, avg=8389.07, stdev=267.96, samples=2896
iops : min= 34, max= 3080, avg=2097.11, stdev=66.98, samples=2896
write: IOPS=902, BW=3608KiB/s (3695kB/s)(1276MiB/362022msec); 0 zone resets
slat (usec): min=18, max=659871, avg=129.87, stdev=3944.62
clat (usec): min=965, max=3245.1k, avg=78459.69, stdev=62441.90
lat (usec): min=1011, max=3245.2k, avg=78590.97, stdev=62552.80
clat percentiles (msec):
| 1.00th=[ 4], 5.00th=[ 9], 10.00th=[ 16], 20.00th=[ 30],
| 30.00th=[ 43], 40.00th=[ 57], 50.00th=[ 70], 60.00th=[ 85],
| 70.00th=[ 100], 80.00th=[ 117], 90.00th=[ 142], 95.00th=[ 163],
| 99.00th=[ 313], 99.50th=[ 418], 99.90th=[ 617], 99.95th=[ 693],
| 99.99th=[ 869]
bw ( KiB/s): min= 264, max= 5200, per=100.00%, avg=3612.32, stdev=113.95, samples=2892
iops : min= 66, max= 1300, avg=902.95, stdev=28.49, samples=2892
lat (usec) : 250=0.01%, 1000=0.01%
lat (msec) : 2=1.36%, 4=4.00%, 10=11.75%, 20=18.39%, 50=37.39%
lat (msec) : 100=17.33%, 250=9.22%, 500=0.45%, 750=0.09%, 1000=0.01%
lat (msec) : 2000=0.01%, >=2000=0.01%
cpu : usr=1.34%, sys=6.10%, ctx=2162632, majf=0, minf=74
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=100.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
issued rwts: total=759404,326573,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=32
Run status group 0 (all jobs):
READ: bw=8391KiB/s (8592kB/s), 8391KiB/s-8391KiB/s (8592kB/s-8592kB/s), io=2966MiB (3111MB), run=362022-362022msec
WRITE: bw=3608KiB/s (3695kB/s), 3608KiB/s-3608KiB/s (3695kB/s-3695kB/s), io=1276MiB (1338MB), run=362022-362022msec
Disk stats (read/write):
mmcblk0: ios=758978/326496, merge=162/186, ticks=20360765/25349924, in_queue=45710782, util=100.00%
2.2.1 IOPS(每秒 I/O 次数):随机工况的核心指标
read: IOPS=2097 ...
write: IOPS=902 ...
- 怎么看:在 4K 随机读写测试中,不要看 BW(带宽)!直接看 IOPS! 随机读写模拟的是系统启动、加载动态库、执行 SQLite 数据库读写等碎片化操作,考验的是主控芯片的寻址能力。
- 测试判定:
- 验真伪:检查比例是否对。2097 / (2097 + 902) ≈ 70%,完美符合我们下发的
--rwmixread=70参数,说明测试工具正常执行了混合指令。 - 看达标:将总计约 3000 的 IOPS 成绩,去和 eMMC 芯片规格书(Spec)或竞品数据进行比对。如果这个值过低,整机在多开应用或频繁存取小文件时会非常卡顿。
2.2.2 读写 clat 平均延迟对比:主控芯片的“消化能力”
read: clat (usec): ... avg=27116.04 (约27ms)
write: clat (usec): ... avg=78459.69 (约78ms)
- 怎么看:注意单位是
usec(微秒,1000微秒=1毫秒)。分别看读和写的平均延迟(avg)。 - 测试判定:写延迟通常会比读延迟大得多(这是 NAND 闪存物理特性决定的),本例中写耗时是读的近 3 倍。测试人员需关注:如果在压测后期,写延迟突然飙升到读延迟的十几倍甚至几十倍,说明 eMMC 内部可用空白块耗尽,主控被迫在做极其耗时的垃圾回收(GC),这代表固件的磨损均衡机制可能不够优秀。
2.2.3 lat (msec) 分布区间:系统的“跟手程度”
lat (msec) : 10=11.75%, 20=18.39%, 50=37.39%, 100=17.33%, 250=9.22%
- 怎么看:这个分布图直接决定了用户感受到的系统有多“流畅”。它表示不同耗时区间的请求占比。本例中,将近 85% 的 I/O 请求都在 10ms ~ 100ms 之间处理完了。
- 测试判定:
- 健康状态:大部分请求集中在左侧(比如 50ms 以内),呈现正态分布。
- 警报状态:如果大量请求(比如超过 10%)堆积在
500ms或更高区间,说明底层发生了严重拥塞。这种情况下,即使平均 IOPS 尚可,上层业务(如数据库写入)也会经常遭遇 Timeout(超时)报错。
2.2.4 util(设备利用率):确信度检查
util=100.00%
- 怎么看:和顺序写一样,必须保证利用率为 100%。由于我们开了 4 个线程(
numjobs=4)并且并发深度是 32,系统发出的请求如狂风骤雨,eMMC 必须处于 100% 满负荷工作状态。 - 测试判定:如果在这里看到 util 没跑满,或者 CPU 的
sys利用率极高甚至打满单核,说明“喂包”的这端(CPU/驱动/内核调度)拉垮了,测出的不是 eMMC 的极限,而是系统 CPU 或驱动的极限。
3 错误日志查看
在使用fio测试EMMC的过程中,需要打开另一个终端,输入以下命令,实时显示系统的内核日志报错,如果无报错输出,则日志项视为PASS
dmesg -wT | grep -iE "mmc|blk|error|fail|timeout|reset"
留下评论