为什么我的百兆SD卡,在Linux开发板上只有20MB/s?—— SD卡性能排查终极指南

在嵌入式Linux开发中,我们经常遇到一个令人沮丧的场景:你买了一张昂贵的、标称100MB/s(UHS-I U3)的SD卡,满怀期望地插到你的开发板(如RK3588, RV1126等)上,结果用dd一测,写入速度只有区区20MB/s。

这是卡的问题,还是板子的问题?

这篇博客将为你提供一个系统性的测试框架,帮助你从“卡片”、“控制器”、“驱动”到“文件系统”逐层排查,最终定位到真正的性能瓶颈。

核心思路:分层测试与瓶颈定位

我们的排查思路很简单:

  1. 基准测试:这张SD卡在“理想状态”(如PC)下的极限速度是多少?

  2. 板级识别检查:板子(SoC)是否以“最高速度模式”识别了这张卡?(这是最常被忽略的瓶颈点

  3. 裸设备测试:Linux系统对SD卡物理块设备的顺序读写能力如何?(绕过文件系统

  4. 文件系统测试:在文件系统上(如ext4)的随机和顺序读写性能如何?(模拟真实应用

  5. 系统负载分析:在I/O测试时,CPU是否已经不堪重负?


第 1 步:建立基准 —— SD卡在PC上的极限速度

在怀疑板子之前,先确认卡是好的。

  • 工具:一台带USB 3.0的电脑,一个高质量的USB 3.0读卡器。

  • 软件CrystalDiskMark (Windows) 或 dd/fio (Linux)。

  • 目的:测出这张卡在“最佳环境”下的顺序和随机I/O性能。

在windows下测试的具体操作方法

  • 将SD卡插入USB 3.0读卡器,连接到PC的USB 3.0端口(通常是蓝色接口)。

  • 下载并打开 CrystalDiskMark软件

  • 在软件界面的右上角,选择正确的盘符(例如 F:\)。

  • 点击左上角的“All”按钮开始测试。

    • 如何解读结果: 测试完成后,你会看到一张表格,但我们只需要关注两行:

      1. SEQ1M Q8T1 (或 SEQ): 顺序读/写性能。这代表你拷贝单个大文件(如视频)时的速度。这个数字就是SD卡包装上标称的“100MB/s”的来源。这是你的“顺序I/O天花板”

      2. RND4K Q1T1 (或 4K): 4KB 随机单队列单线程读/写性能。这代表操作系统在卡上读写大量小文件(如系统日志、配置文件、数据库)时的速度。这个速度通常很低(例如 0.5MB/s ~ 3MB/s),但它对系统启动速度、应用打开速度等日常流畅度的影响,比顺序性能大得多!

把上面的测试结果记录下来

  • 顺序天花板 (SEQ1M Q1T1): 读 95.95 MB/s / 写 38.08 MB/s

    • (这模拟了你拷贝一个大电影文件的速度)

  • 随机天花板 (RND4K Q1T1): 读 9.19 MB/s / 写 3.34 MB/s

    • (这模拟了你系统启动和日常卡顿的程度)

在Linux下测试的具体操作方法

  • 操作方法(假设SD卡设备为 /dev/sdb):

    警告:直接对 /dev/sdb 操作会清除所有数据!建议对分区 /dev/sdb1 挂载后在文件系统上测试,或确保是测试卡。

  • 模拟 CrystalDiskMark 的测试(在文件系统上): 假设SD卡挂载在 /media/user/sdcard

    # 1. 测试顺序写 (类似 SEQ1M)
    fio --name=seqwrite --directory=/media/user/sdcard --size=1G \
        --rw=write --bs=1M --direct=1 --ioengine=libaio --group_reporting
    
    # 2. 测试顺序读 (类似 SEQ1M)
    fio --name=seqread --directory=/media/user/sdcard --size=1G \
        --rw=read --bs=1M --direct=1 --ioengine=libaio --group_reporting --fdatasync=1
    
    # 3. 测试 4K 随机写 (类似 RND4K Q1T1)
    fio --name=randwrite --directory=/media/user/sdcard --size=256M \
        --rw=randwrite --bs=4k --direct=1 --ioengine=libaio --group_reporting --numjobs=1 --iodepth=

记录测试结果

记下顺序读写速度和随机读写速度,把这份数据当作sd卡本身的最大性能有了这份数据,当你的开发板在后续步骤中测出“顺序写 20.5MB/s”时,你就有了确凿的证据:性能损失了约 77%。你的排查目标将非常明确:找出丢失的这 69MB/s 到底被谁(DTS配置、驱动、CPU瓶颈)吃掉了。

第 2 步:关键排查 —— 板子究竟如何“看待”这张SD卡?

在 PC 上,我们验证了卡的“理想性能”。现在,我们要在板子上验证“实际性能”。性能不佳的第一个、也是最常见的瓶颈,就出在板子的SoC(系统级芯片)与SD卡之间的“沟通方式”上。

这个“沟通方式”就是 SD 卡总线速度模式 (Bus Speed Mode)

1. 什么是“速度协商”?

当你把SD卡插入开发板时,一个由硬件和软件(内核驱动)共同参与的“协商”过程就开始了:

  1. 内核驱动 (mmc driver) 被唤醒,它通过SoC的SDMMC控制器(硬件)给卡上电。

  2. 驱动问卡:“你好,你是谁?你支持哪些速度模式?” (读取卡的CSD/SCR寄存器)

  3. 卡片回复:“我是UHS-I卡,我最快支持SDR104模式 (104MB/s),也兼容老的HS模式 (25MB/s) 等。”

  4. 驱动转头看自己的“配置图”(Device Tree),心想:“老板(DTS)告诉我,这块板子的硬件电路(pinmux, 供电)支持哪些模式?”

  5. 最终决策:驱动会取一个“交集”——在卡片支持的、且DTS允许的模式中,选择最高的那一个来运行。

瓶颈就在这里:如果你的卡支持 104MB/s,但你的DTS文件里压根没写 SDR104,或者干脆写了 no-1-8-v(禁止切换到UHS所需的1.8V电压),那么驱动就只能被迫选择那个“老的”、“慢的” 25MB/s HS 模式。

2. 如何检查协商结果?—— dmesg 内核日志

内核会在协商完成后,把结果打印到启动日志中。这是我们必须查看的第一手证据

在板子启动后,或插入SD卡后,立刻在终端执行:

dmesg | grep -i "mmc"

提示:在很多开发板上(特别是Rockchip),mmc0 可能是板载的eMMC,而 mmc1mmc2 才是外置的SD卡插槽。你需要根据日志上下文来判断哪个是你的目标。

你需要在这堆日志里,像侦探一样找出那行最关键的描述:

dmesg 日志中看到的关键字模式信号电压理论最高速度性能等级
mmc0: new high speed ... cardHS (High Speed)3.3V25 MB/s慢 (最常见降速)
mmc0: new high speed DDR50 ... cardDDR503.3V50 MB/s⚠️ 中等 (不常见)
mmc0: new ultra high speed SDR50 ...UHS-I SDR501.8V50 MB/s✅ 快
mmc0: new ultra high speed SDR104 ...UHS-I SDR1041.8V104 MB/s目标模式
mmc0: new ultra high speed HS200 ...HS200 (eMMC/SD)1.8V200 MB/s🚀 极快 (需特定支持)
3. 排查结果:定位瓶颈

现在,对比你的日志和上表:

情况A (坏):你看到了 new high speed card (25MB/s)

  • 诊断:恭喜你,你100%找到了性能瓶颈!你的板子工作在了降速模式。无论你用 dd 怎么测,速度上限都被锁死在了 25MB/s。

  • 主要原因Device Tree (DTS/DTB) 配置不当。

  • 解决方案

    1. 打开你的DTS文件(例如 rk3588-orangepi-5-plus.dts)。

    2. 找到对应的 sdmmc 节点(例如 &sdmmc0&sdmmc1)。

    3. 检查:是否缺少UHS模式的属性?

      • 必须有 bus-width = <4>;(4线模式)。

      • 必须有 sd-uhs-sdr104;sd-uhs-sdr50; 等属性来声明支持UHS。

    4. 重点检查:是否错误地包含了 no-1-8-v; 这个属性?

      • 这是一个“性能杀手”。它明确告诉内核“禁止切换到1.8V”。

      • 所有UHS模式(SDR50/SDR104)都必须工作在1.8V。一旦禁止,驱动就只能退回到3.3V的 HS 模式 (25MB/s)。

    5. 修改DTS,重新编译并烧录 dtb,然后重启,再次 dmesg 检查。

情况B (好):你看到了 new ultra high speed SDR104 (104MB/s)

  • 诊断:非常好。这说明板子的硬件、DTS配置、内核驱动都已正确工作,并成功将SD卡协商到了最高速度模式。

  • 下一步:如果在这种情况下,你的 ddfio 测试速度还是很慢(例如只有 40MB/s),那么瓶颈就不在DTS配置,而在别处。你需要进入后面的第3步(裸设备测试)和第4步(文件系统测试),去排查Linux驱动的I/O效率、文件系统开销、或者CPU瓶颈。

4. 高级技巧:实时检查 /sys 文件系统

dmesg 只记录了“启动时”的协商结果。如果你想实时查看当前卡片的工作状态,sysfs 是更好的工具。

# 1. 找到你的mmc主机 (假设是 mmc1)
cd /sys/class/mmc_host/mmc1

# 2. 你的卡信息在 mmc1:xxxx 目录中 (xxxx是卡片ID)
#    用 ls 找到它,例如:
ls
#    输出: mmc1:0001 ... (就进入这个目录)
cd mmc1:0001

# 3. 查看卡片信息
cat name        # 卡片名字
cat cid         # 卡片ID
cat csd         # 卡片能力
cat scr         # SD卡配置

# 4. 最关键的:回到上一层,看主机的I/O状态
cd ..
cat ios

执行 cat ios 后,你会看到类似这样的输出:

clock:          208000000 Hz  (208 MHz)
actual clock:   200000000 Hz  (200 MHz)
...
signal voltage: 1.80 V
timing spec:    sd uhs sdr104
bus width:      4 bit

这份“体检报告”一目了然:时钟 200MHz(接近SDR104的208MHz),电压 1.80V,时序模式 sdr104,总线位宽 4 bit。这有力地证明了系统正处于最佳性能状态。

第 3 步:裸设备测试(绕过文件系统)

目的:这一步是“压力测试”。我们要搞清楚 SoC的SDMMC控制器 + Linux驱动 这条硬件路径的I/O吞吐极限。

为此,我们必须绕过文件系统(如ext4)这一层软件。文件系统会带来额外的开销,比如日志(journaling)、元数据更新、块分配策略等,这些都会干扰我们对底层硬件性能的判断。

工具dd 操作:直接读写SD卡的块设备节点(如 /dev/mmcblk0)。

⚠️ 终极警告:这会立即、彻底地摧毁你SD卡上的所有数据! 你的分区表、所有文件都会瞬间消失。请只在100%确认是可牺牲的测试卡时才执行此操作。

# 假设 /dev/mmcblk0 是你的SD卡设备节点

# 1. 测试顺序写 (使用 direct 绕过缓存)
#    if=/dev/zero: 一个虚拟的"数据源",以最快速度提供0字节数据
#    bs=1M:       使用1MB的大块来测试顺序吞吐量
#    count=100:   总共写入 100 * 1M = 100MB 数据
#    oflag=direct:【关键】绕过Linux页面缓存(RAM),强制数据直接写入硬件
dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=100 oflag=direct

# 2. 测试顺序读 (先清空缓存)
#    echo 3 > ...: 强制内核丢弃所有缓存,确保我们读的是物理卡
echo 3 > /proc/sys/vm/drop_caches
#    iflag=direct:【关键】同样绕过缓存,强制从硬件读取
dd if=/dev/mmcblk0 of=/dev/null bs=1M count=100 iflag=direct
排查结果
  • 如果裸设备速度(例如 90MB/s)接近第1步的基准(例如 95MB/s)

    • 诊断:完美。你的硬件(SoC)、DTS配置、Linux驱动程序都工作正常且高效。

    • 下一步:如果你的应用依然感觉慢,那么瓶颈 100% 在第 4 步,即文件系统或应用层。

  • 如果裸设备速度(例如 40MB/s)远低于基准,但第2步的模式(SDR104)是正确的

    • 诊断:这是一个深层问题。说明瓶颈出在Linux的 sdmmc 驱动效率(比如DMA未正常工作,降级为CPU拷贝的PIO模式)或SoC的SDMMC控制器硬件设计本身(它虽然协商上了SDR104,但内部总线就是跑不满)。这是最难解决的硬件/驱动问题。


第 4 步:板级性能测试(基于文件系统)

目的:现在我们在实际生产中的真实场景下进行测试。这一步测试的是你的应用程序(如跑在ext4上的Python脚本或C++程序)实际能体验到的读写速度。这一层包含了文件系统带来的所有开销。

工具一:dd (测试顺序读写) 这是对“拷贝大文件”场景的模拟

# 假设SD卡已格式化为ext4,并挂载在 /mnt/sdcard

# 1. 测试文件系统顺序写 (必须加 oflag=sync)
#    oflag=sync:【最关键参数】
#    没有它, dd会把数据写入RAM缓存(速度飞快,几百MB/s)然后立即退出
#    sync会强制dd等待,直到数据从RAM被真正刷入(commit)到物理SD卡上
#    这才是真实、安全的写入速度。
dd if=/dev/zero of=/mnt/sdcard/testfile bs=1M count=100 oflag=sync

# 2. 测试文件系统顺序读 (必须先清空缓存)
sudo echo 3 > /proc/sys/vm/drop_caches
dd if=/mnt/sdcard/testfile of=/dev/null bs=1M count=100

# 3. 清理
rm /mnt/sdcard/testfile

工具二:fio (测试随机读写) 系统启动、读写日志、数据库操作……这些都不是顺序读写,而是琐碎的随机I/Odd 在这里毫无用处,fio 才能衡量这个“卡顿”的元凶。

# 假设 fio 已安装,在 /mnt/sdcard 目录下测试

# 1. 测试 4K 随机写入
#    rw=randwrite: 随机写入模式
#    bs=4k:        使用4K小块, 这是文件系统和OS最常用的I/O单元
#    direct=1:     同样绕过缓存, 测算物理I/O
#    size=256M:    在256MB大小的文件范围内进行随机读写
fio -directory=/mnt/sdcard -name=randwrite_test \
    -ioengine=libaio -direct=1 -rw=randwrite -bs=4k \
    -size=256M -numjobs=1 -runtime=60 -group_reporting

# 2. 测试 4K 随机读取
fio -directory=/mnt/sdcard -name=randread_test \
    -ioengine=libaio -direct=1 -rw=randread -bs=4k \
    -size=256M -numjobs=1 -runtime=60 -group_reporting
排查结果

fio 的结果会告诉你两个关键指标:

  1. bw (Bandwidth):带宽 (MB/s)。

  2. IOPS (Input/Output Operations Per Second):每秒读写次数。

  • 如果第3步(裸)很快,但第4步dd(文件)很慢

    • 诊断:文件系统开销过大。ext4 的日志功能在写入时会带来不小的性能损失。

    • 解决:可以尝试更换文件系统(如 f2fs,专为闪存优化),或者在挂载 ext4 时使用特定选项(如 noatime, nodiratime, data=writeback)来减少开销(但这可能牺牲数据安全性)。

  • 如果 4K 随机 IOPS 非常低(例如低于 100)

    • 诊断:这直接解释了为什么你的系统启动慢、应用打开卡顿。

    • 解决:这是闪存介质(SD卡)的物理特性。除了换一张A1/A2等级(明确标称高IOPS)的卡之外,别无他法。这也是为什么eMMC(通常IOPS更高)的系统体验远超SD卡。

第 5 步:终极排查 —— 是不是CPU在拖后腿?

有时候,卡和控制器都没问题,但I/O速度就是上不去。为什么?

因为CPU太忙了!

当I/O请求非常密集时,CPU需要花费大量时间来处理中断和数据拷贝(kworker进程或 irq/... 进程)。如果你在fio测试的同时,CPU的一个核已经被占满(100%),那么瓶颈就不在SD卡,而在CPU。

  • 工具tophtop

  • 操作

    1. 打开一个SSH终端,运行 top

    2. 打开第二个SSH终端,运行 fio 随机读写测试。

    3. 观察第一个终端 top 的输出。

排查结果: 如果在fio运行时,你看到 %cpu 占用率极高,特别是 si (software interrupt) 或 wa (iowait) 飙升,或者某个 kworker 进程占满CPU,这说明:

板子的CPU处理能力已经跟不上SD卡的I/O速度了。

这不是SD卡的问题,而是系统的I/O子系统(可能是驱动,也可能是CPU本身)的瓶颈。

结论:我的诊断清单

当再遇到“SD卡慢”的问题时,请拿出这张清单:

  1. [基准] 这张卡在PC上能跑多快?(测试sd卡自身的能力极限是多少)

  2. [DTS] dmesg 显示的SD卡工作模式是什么?(SDR104 还是 HS?)

  3. [驱动] dd 读写裸设备 /dev/mmcblk0 速度快吗?

  4. [文件系统] dd ... oflag=sync 读写文件系统的速度快吗?

  5. [应用] fio 测试的 4K 随机 IOPS 是多少?

  6. [CPU] fio 运行时,top 里的CPU是否已经 100% 占满了?

通过这一套组合拳,你几乎可以100%定位到SD卡性能的真正瓶颈。对于嵌入式Linux开发板来说,最常见的问题永远是第2步——Device Tree配置不当,导致SD卡工作在了降速模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值