4 分钟阅读 次阅读

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 数据库读写等碎片化操作,考验的是主控芯片的寻址能力。
  • 测试判定
  1. 验真伪:检查比例是否对。2097 / (2097 + 902) ≈ 70%,完美符合我们下发的 --rwmixread=70 参数,说明测试工具正常执行了混合指令。
  2. 看达标:将总计约 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"

留下评论