Linux下eMMC与SD卡底层管理:从分区格式化到故障排查实战

📅 2026/8/2 23:49:02 👁️ 阅读次数 📝 编程学习
Linux下eMMC与SD卡底层管理:从分区格式化到故障排查实战

1. 项目概述:为什么我们需要掌握存储介质的底层管理?

在嵌入式开发和Linux系统运维的日常里,我们打交道最多的硬件,除了CPU和内存,恐怕就是各种非易失性存储设备了。eMMC和SD卡,这两者看似普通,却是无数智能设备、工控主板、物联网终端乃至个人单板计算机(如树莓派)的“数据心脏”。我遇到过太多这样的场景:一块全新的核心板需要烧写系统镜像、一个旧设备的存储空间需要重新规划分区、或者SD卡在Windows下被误操作后无法在Linux中识别。每当这时,如果只会用图形化工具点几下,一旦遇到复杂情况就会束手无策。掌握在Linux命令行下对eMMC和SD卡进行格式化、分区等底层管理操作,不是一项炫技,而是一项实实在在的、能让你从“使用者”变为“掌控者”的核心技能。

这不仅仅是运行几个命令那么简单。它意味着你能理解存储设备在系统里的抽象层次(从块设备到文件系统),能根据应用需求(比如为嵌入式系统划分独立的boot、rootfs、data分区)设计合理的分区方案,并能在设备出现异常时,进行有效的诊断和修复。无论是为RK3562这样的国产主控规划eMMC布局,还是为ESP32-P4搭配LVGL文件系统准备SD卡,其底层逻辑都是相通的。接下来,我将以一个资深嵌入式开发者的视角,拆解从设备识别到最终挂载使用的完整流程,并分享那些手册上不会写的“踩坑”经验和操作细节。

2. 核心概念与准备工作:认识你的存储设备

在动手之前,我们必须先理清几个关键概念,并准备好操作环境。盲目操作有损坏设备和丢失数据的风险。

2.1 eMMC与SD卡的异同

很多人会把eMMC和SD卡混为一谈,虽然它们协议同源(都基于MMC标准),但在实际使用中区别很大。

  • eMMC:可以理解为“焊死在板子上的SD卡”。它把存储芯片和控制器封装在一起,通过BGA焊盘直接集成在主板上。在Linux系统中,它通常被识别为一个固定的块设备,比如/dev/mmcblk0。它的特点是稳定、速度快(尤其是eMMC 5.1协议及以上),常用于手机、平板、机顶盒和各类嵌入式工控设备。你无法像拔插SD卡一样轻易更换它。
  • SD卡/TF卡:是可移动的存储介质,通过卡槽连接。在Linux中,插入后通常被识别为/dev/sdX(如sdb, sdc)或/dev/mmcblk0(取决于主控驱动)。它的特点是灵活、便携,但长期擦写寿命和稳定性通常不如eMMC。

重要提示:在进行任何分区或格式化操作前,务必、务必、务必确认你操作的是正确的设备节点。误操作系统盘(如/dev/sda)会导致灾难性后果。一个简单的记忆法:即将插入SD卡前,先用lsblkls /dev/sd*查看现有设备;插入后再查看一次,多出来的那个就是你的SD卡。

2.2 操作前的环境与工具准备

你需要一个运行Linux的环境。这可以是:

  1. 一台物理Linux电脑(如Ubuntu桌面版)。
  2. 一个虚拟机(如VirtualBox中的Linux)。
  3. 最常用的场景:通过串口或SSH登录到你的嵌入式开发板或设备。

所需的工具几乎都包含在主流Linux发行版中:

  • lsblk:列出所有块设备(磁盘、eMMC、SD卡)的树状视图,是最直观的查看工具。
  • fdisk/parted/gdisk:分区工具。fdisk适用于传统的MBR分区表(最大2TB,最多4个主分区);parted功能更强大,支持交互和脚本操作,且完美支持GPT分区表;gdisk则是专门用于GPT分区表的工具。
  • mkfs:格式化工具家族,如mkfs.ext4,mkfs.vfat等,用于在分区上创建文件系统。
  • dd:强大的块设备拷贝工具,常用于烧录镜像或彻底擦除设备。
  • blkid:查看块设备的文件系统类型、UUID等属性。
  • mount/umount:挂载和卸载文件系统。

在嵌入式环境中,busybox可能只提供了这些工具的简化版,但基本功能是齐全的。

3. 设备识别与状态确认:找到并认清你的目标

这是所有操作的第一步,也是最关键的一步,绝不能出错。

3.1 使用lsblk进行可视化识别

插入SD卡或确认你的设备已上电并连接后,在终端输入:

lsblk

你会看到类似下面的输出:

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 465.8G 0 disk ├─sda1 8:1 0 512M 0 part /boot/efi ├─sda2 8:2 0 732M 0 part /boot └─sda3 8:3 0 464.6G 0 part / mmcblk0 179:0 0 29.7G 0 disk ├─mmcblk0p1 179:1 0 256M 0 part └─mmcblk0p2 179:2 0 29.5G 0 part
  • sda是我的系统硬盘,下面有三个分区。
  • mmcblk0就是我的eMMC或SD卡(在这个例子中,它被识别为mmcblk设备)。它下面有两个分区:mmcblk0p1mmcblk0p2
  • RM列为1表示可移动设备(如USB或SD卡),为0表示固定设备(如硬盘、eMMC)。
  • SIZE显示了总容量。
  • MOUNTPOINTS显示了分区当前挂载的位置。如果这里有挂载点,在进行写操作前必须先卸载。

3.2 使用dmesg查看内核识别日志

当你插入一个SD卡时,内核会输出识别日志。快速查看最新日志的方法是:

sudo dmesg | tail -20

你可以看到类似[sdb] 625142448 512-byte logical blocks: (320 GB/298 GiB)这样的信息,明确告诉你新设备被识别为/dev/sdb以及它的容量。

3.3 确认设备详情与卸载分区

使用blkid可以查看分区的文件系统类型和UUID:

sudo blkid /dev/mmcblk0p1

如果设备有分区被自动挂载了(比如在桌面环境中),你必须先卸载它才能进行修改:

# 假设 /dev/mmcblk0p1 挂载到了 /media/user/BOOT sudo umount /media/user/BOOT # 或者,更暴力但有效的是卸载整个设备的所有分区 sudo umount /dev/mmcblk0*

4. 分区表操作:规划你的存储空间

清除了操作障碍,现在我们来重新规划存储空间。这涉及到分区表的操作。

4.1 选择分区工具:fdisk vs parted

对于新手,我推荐从fdisk开始,它的交互提示比较友好。但对于现代大容量设备(>2TB)或需要复杂分区(超过4个主分区)的场景,parted是更好的选择,它原生支持GPT分区表。

场景选择指南:

  • SD卡/U盘,容量小于2TB,分区简单:使用fdisk
  • eMMC/大容量SSD,或需要GPT分区表:使用partedgdisk
  • 嵌入式系统,需要脚本化自动化分区:使用parted的非交互模式或sfdisk

4.2 使用fdisk进行MBR分区实战

假设我们要操作的设备是/dev/sdb(请再次确认!)。

  1. 启动fdisk
    sudo fdisk /dev/sdb
  2. 删除旧分区(如果需要):进入交互界面后,输入p打印现有分区表。确认无误后,输入d删除分区,根据提示选择分区号,直到所有分区被删除。
  3. 创建新分区表:输入o创建一个新的DOS(MBR)分区表。如果你想用GPT,则输入g
  4. 创建新分区
    • 输入n创建新分区。
    • 选择分区类型:p为主分区,e为扩展分区。对于SD卡系统盘,第一个分区通常是主分区。
    • 设置分区号(1-4)。
    • 设置起始扇区:一般直接回车使用默认值(第一个可用扇区)。
    • 设置结束扇区:这里可以输入+size{K,M,G},例如+256M表示创建一个256MB的分区。这对于规划固定大小的boot分区非常方便。
  5. 更改分区类型:某些分区需要特定的类型ID。例如,一个用于树莓派boot的FAT32分区,其类型应该是“W95 FAT32 (LBA)”,ID是c
    • 输入t,选择分区号,然后输入c
    • 输入L可以列出所有已知的类型代码。
  6. 保存并退出:输入p最后确认分区表布局。确认无误后,输入w将更改写入磁盘并退出。输入w之前,所有操作都只在内存中,一旦输入w就无法撤销了。

4.3 使用parted进行GPT分区实战(更现代的方式)

parted功能强大,支持更直观的大小指定。

  1. 启动parted
    sudo parted /dev/sdb
  2. 创建GPT分区表
    (parted) mklabel gpt

    警告mklabel会摧毁磁盘上所有现有数据。

  3. 创建分区parted的命令更简洁。
    # 创建一个从1MB开始,到257MB结束的FAT32 boot分区(即256MB大小) (parted) mkpart primary fat32 1MiB 257MiB # 将分区1的标签设置为“BOOT” (parted) name 1 BOOT # 创建一个从257MB开始,到磁盘末尾的ext4根文件系统分区 (parted) mkpart primary ext4 257MiB 100% (parted) name 2 ROOTFS
    注意这里使用了MiB%作为单位,非常直观。1MiB的起始偏移是为了满足一些系统的分区对齐要求,有利于提升性能。
  4. 查看与退出
    (parted) print (parted) quit

实操心得:在嵌入式Linux中,我强烈建议使用GPT分区表,即使容量不大。因为它没有MBR的4个主分区限制,而且分区表有备份,更安全。许多现代Bootloader(如U-Boot)对GPT的支持也很好。

5. 文件系统创建与格式化:赋予分区灵魂

分区只是划好了“地盘”,文件系统才是管理地盘上“房屋和道路”的规则。不同的用途需要不同的文件系统。

5.1 常见文件系统选型

  • FAT32 / exFAT
    • FAT32:兼容性之王。几乎所有操作系统、BIOS、Bootloader都能识别。最大单文件限制为4GB。常用于boot分区(存放内核、设备树)、在设备和Windows/Mac间共享数据的分区。
    • exFAT:微软推出,专为大容量闪存设计。突破了4GB文件限制,兼容性也很好(现代系统均支持)。适合存放大型镜像、视频等。
  • ext4:Linux世界的标准。日志式文件系统,稳定、高效、功能完整(权限、软硬链接等)。是Linux根文件系统(rootfs)和数据分区的首选。
  • F2FS:专为NAND闪存(如eMMC、SD卡、SSD)设计的文件系统。在频繁擦写的场景下,能减少写入放大,延长闪存寿命并提升性能。非常适合作为IoT设备的数据日志分区。
  • Btrfs / ZFS:高级文件系统,支持快照、压缩、RAID等。在普通SD卡/eMMC管理上用得较少,更适用于NAS或服务器。

5.2 格式化命令实操

创建好分区后,系统会生成对应的设备节点,如/dev/sdb1,/dev/sdb2

  1. 格式化boot分区为FAT32

    sudo mkfs.vfat -F 32 -n BOOT /dev/sdb1
    • -F 32:指定FAT32格式。
    • -n BOOT:为分区设置卷标(label),方便识别。在挂载时,可以通过LABEL=BOOT来挂载,比用设备号更稳定。
  2. 格式化根分区为ext4

    sudo mkfs.ext4 -L ROOTFS /dev/sdb2
    • -L ROOTFS:设置ext4分区的卷标。
  3. (可选)格式化数据分区为F2FS

    # 首先确保系统支持f2fs,可能需要安装 mkfs.f2fs sudo mkfs.f2fs -l DATA /dev/sdb3
    • -l DATA:设置F2FS分区的卷标。

注意事项:对于eMMC,尤其是工业级eMMC,在首次使用或希望彻底恢复性能时,可以进行安全擦除(Secure Erase)TRIM。但这通常需要在驱动或硬件层面支持,且操作有风险。更常见的做法是用dd写零:

# 谨慎操作!这将清空整个设备的所有数据。 sudo dd if=/dev/zero of=/dev/mmcblk0 bs=1M status=progress

这个操作非常耗时,但对于清除旧分区表残留、解决一些诡异的“使用驱动器中的光盘之前需要将其格式化”类问题有时有奇效。

6. 挂载、验证与自动化

格式化完成后,我们需要挂载分区来验证和使用它们。

6.1 手动挂载与验证

# 创建挂载点目录 sudo mkdir -p /mnt/boot /mnt/rootfs # 挂载分区 sudo mount /dev/sdb1 /mnt/boot sudo mount /dev/sdb2 /mnt/rootfs # 查看挂载结果 df -h /mnt/boot /mnt/rootfs # 在挂载点内进行读写测试 sudo touch /mnt/boot/test.txt sudo ls -la /mnt/boot/ # 卸载分区 sudo umount /mnt/boot /mnt/rootfs

6.2 通过/etc/fstab实现自动挂载

对于嵌入式设备或需要固定挂载的场景,可以编辑/etc/fstab文件。但更推荐使用UUID 或 LABEL来标识分区,而不是/dev/sdb1这种可能变动的设备名。

  1. 获取分区的UUID和LABEL:

    sudo blkid

    输出示例:/dev/sdb1: LABEL="BOOT" UUID="ABCD-1234" TYPE="vfat"

  2. 编辑/etc/fstab,添加类似如下行:

    # <file system> <mount point> <type> <options> <dump> <pass> UUID=ABCD-1234 /boot vfat defaults,noatime 0 2 LABEL=ROOTFS / ext4 defaults,noatime 0 1
    • noatime:禁止记录文件访问时间,可以减少对存储设备的写操作,延长闪存寿命,非常适合eMMC/SD卡。
    • pass:根分区设为1,其他分区设为2。控制fsck磁盘检查的顺序。

7. 高级话题与故障排查实录

掌握了基本流程,我们来看看那些容易踩坑的地方和进阶技巧。

7.1 嵌入式系统典型分区布局设计

一个标准的嵌入式Linux系统SD卡/eMMC镜像,其分区布局通常如下:

分区序号设备节点示例大小文件系统用途备注
1/dev/mmcblk0p116-256 MBFAT32Boot分区存放U-Boot、SPL、内核镜像(zImage/Image)、设备树(.dtb)、启动脚本。必须为FAT(或ext2 for U-Boot),且通常需要设置为“可启动”标志。
2/dev/mmcblk0p2剩余全部或固定大小ext4根文件系统存放完整的Linux根目录。
3/dev/mmcblk0p3512 MB - 几 GBext4 或 F2FS数据/用户分区存放应用数据、日志、用户文件。与根分区分离,便于系统升级时保留数据。
(可选)4/dev/mmcblk0p4等于内存大小(swap)交换分区在内存紧张的设备上启用,但频繁读写会损伤闪存,嵌入式设备不推荐。

设计要点:Boot分区不宜过大,够用即可。根文件系统分区的大小取决于你的Rootfs镜像大小,预留20%左右空间给系统和日志增长。数据分区可以根据需要调整。

7.2 常见问题与解决方案速查表

问题现象可能原因排查与解决思路
插入SD卡后,lsblk看不到设备1. 物理接触不良
2. 卡槽或读卡器故障
3. 卡已物理损坏
4. 分区表严重损坏,内核无法识别
1. 重新插拔,换卡槽或读卡器。
2. 在另一台电脑上测试。
3. 使用dmesg | tail查看内核是否有错误信息。
4. 尝试用sudo fdisk -l强制扫描。
设备能看到(如sdb),但无分区(如sdb1分区表丢失或损坏使用sudo parted /dev/sdb print查看。如果显示“unrecognised disk label”,则需要用mklabel重新创建分区表。
分区存在,但无法挂载,提示“wrong fs type”文件系统损坏或类型不匹配1. 用sudo blkid /dev/sdb1查看其声称的类型。
2. 尝试用sudo fsck.vfat /dev/sdb1sudo fsck.ext4 -y /dev/sdb1修复文件系统。修复前最好先备份数据。
Windows提示“使用驱动器X:中的光盘之前需要将其格式化”通常是分区表或文件系统头信息损坏,Windows无法识别,但Linux可能可以1.切勿在Windows中点格式化!
2. 在Linux下用fsck尝试修复。
3. 如果数据不重要,直接在Linux下用fdisk重新分区并格式化。
写入速度极慢1. 卡本身质量差(Class 10以下)。
2. 使用了低效的文件系统(如FAT32写小文件慢)。
3. 没有4K对齐(针对高级格式闪存)。
1. 使用dd测试原始写入速度:dd if=/dev/zero of=/mnt/test.bin bs=1M count=100 oflag=direct
2. 对于数据分区,考虑换用ext4或F2FS。
3. 用parted创建分区时,起始扇区从1MiB(2048扇区)开始,这能保证4K对齐。
嵌入式设备无法从eMMC/SD卡启动1. Bootloader未正确安装或损坏。
2. Boot分区不是活动/可启动分区。
3. 内核镜像或设备树文件缺失/错误。
4. 启动介质顺序不对。
1. 确认使用厂商提供的工具(如RKDevTool, fastboot)或dd正确烧写了Bootloader到设备起始扇区。
2. 在fdisk中用a命令设置boot分区为活动分区。
3. 检查boot分区内文件是否齐全、命名是否正确(与bootloader配置匹配)。
4. 检查硬件启动拨码开关或U-Boot环境变量。

7.3 使用dd命令烧录系统镜像

这是嵌入式开发中最常见的操作之一:将一个预编译好的.img系统镜像直接写入SD卡/eMMC。

# 假设镜像文件是 raspbian.img,SD卡是 /dev/sdb # 首先,确保所有分区都已卸载 sudo umount /dev/sdb* # 使用dd命令写入。if=输入文件,of=输出设备,bs=块大小 sudo dd if=raspbian.img of=/dev/sdb bs=4M status=progress conv=fsync
  • bs=4M:设置块大小,增大可以提升写入速度,但并非越大越好,4M或8M是常用值。
  • status=progress:显示写入进度(较新的dd版本支持)。
  • conv=fsync:确保数据完全同步到物理设备后再返回,保证烧录完整性。

致命警告dd命令非常强大且危险,务必、反复确认of=后面的设备路径是否正确。写错目标会瞬间摧毁其他磁盘的数据。

我个人在实际操作中,形成了一个肌肉记忆:在执行任何针对/dev/sdX/dev/mmcblkX的破坏性命令前,会先用lsblk确认两遍,并且如果设备有多个分区,会先尝试卸载和操作其中一个分区来二次确认设备名。对于dd命令,我甚至会先写一个小的测试文件来验证路径。这些习惯帮我避免了很多潜在的灾难。最后,记住一点:在Linux下,你对硬件拥有至高无上的控制权,这份权力也意味着你需要承担与之匹配的谨慎和责任。