☰
Linux 存储分区扩容实战:从 mmcblk0 识别到 resize2fs 在线扩展
2026/10/9 8:16:50 网站建设 项目流程

1. 从一个真实的翻车现场说起

手里有一块嵌入式板子,eMMC 标称 8GB,系统跑起来之后df -h一看,根分区只有 3.2GB,剩下几个 G 不知道去哪了。这种场景做嵌入式 Linux 的人基本都遇到过——镜像烧录时分区表写死了,或者出厂镜像本身就没把分区扩满。你可能会想,直接fdisk删了重建不就行了?但根分区正在使用中,删了系统就崩了。

这就是Linux 设备存储分区问题处理的典型场景。它涉及的核心链路是:块设备识别(mmcblk0)→ 分区表解析 → 分区扩容 → 文件系统在线扩展(resize2fs)。每一步都有坑,每一步踩错都可能导致数据丢失或者设备变砖。

这篇文章适合谁看?如果你在做嵌入式 Linux 开发、运维国产化设备、或者手里有一台存储空间“缩水”的 Linux 机器想把它救回来,那接下来的内容基本能覆盖你 90% 的需求。我会从分区表的底层逻辑讲起,把 mmcblk0 这类设备的命名规则、分区表类型的选择、resize2fs 的在线扩容原理,以及实操中那些文档里不会写的坑,全部拆开讲清楚。

先给一个全局认知:Linux 下存储设备的分区处理,本质上是三层的协作——块设备层(内核识别硬件)→ 分区表层(MBR/GPT 描述布局)→ 文件系统层(ext4/xfs 管理数据)。很多人出问题就出在只动了其中一层,另外两层没同步,结果就是分区表改了但内核没重读,或者分区扩了但文件系统没跟着扩。下面按这个逻辑逐层拆解。

2. 块设备与分区表:先把底层逻辑搞清楚

2.1 mmcblk0 到底是什么,为什么不是 sda

Linux 下块设备的命名不是随便起的,它直接反映了设备的总线类型和连接方式。你看到sda、nvme0n1、mmcblk0这些名字,其实是在告诉你这个盘是怎么接到系统上的。

  • sda:SATA/SCSI/USB 存储设备,走的是 SCSI 子系统
  • nvme0n1:NVMe 固态硬盘,走 PCIe 直连
  • mmcblk0:MMC/SD/eMMC 设备,走 MMC 子系统

mmcblk0这个命名拆开看:mmc是 MultiMediaCard 子系统的缩写,blk表示块设备,0是设备序号。如果是第二块 eMMC,就是mmcblk1。而这块设备上的分区,命名规则是mmcblk0p1、mmcblk0p2、mmcblk0p3……注意那个p,它是 partition 的分隔符。

这里有个容易搞混的点:整盘设备是mmcblk0,分区是mmcblk0p1。你在fdisk里操作的是mmcblk0,但挂载和扩容针对的是mmcblk0p1这种分区设备。我见过有人对着mmcblk0直接跑resize2fs,报错说“Bad magic number in super-block”,就是因为整盘上没有文件系统,文件系统在分区里。

提示:用lsblk可以一眼看清设备树结构,比fdisk -l更直观。lsblk -f还能同时显示文件系统类型和 UUID。

2.2 分区表类型:MBR 和 GPT 的选择逻辑

分区表是存放在磁盘最前面的一小块区域,用来描述“这块盘被分成了几个区、每个区从哪个扇区到哪个扇区”。Linux 下主流就两种:MBR(DOS)和GPT。

对比项MBRGPT
最大支持磁盘容量2TB约 9.4ZB
最大分区数4 个主分区(或 3 主+1 扩展)128 个(Windows 限制,Linux 更多)
分区表备份无有,头部和尾部各一份
兼容性极好,老设备通吃需要 UEFI 或较新内核
嵌入式常见度非常高逐渐增多

嵌入式设备里 MBR 依然占大头,原因很简单:BootROM 和 U-Boot 的解析代码简单。很多 SoC 的启动流程里,ROM Code 只认 MBR 那 512 字节的分区表,你换成 GPT 它直接不启动。所以你在 mmcblk0 上看到 MBR 分区表,不要觉得“落后”,这是启动链路的硬约束。

但 MBR 有个致命限制:主分区最多 4 个。如果你需要更多分区,就得用扩展分区+逻辑分区的方案。扩展分区占一个主分区槽位,里面可以再切多个逻辑分区。mmcblk0p1到mmcblk0p4是主分区,mmcblk0p5开始就是逻辑分区了。这个编号规则要记住,后面扩容时如果搞错了分区号,操作就会落到错误的分区上。

2.3 分区表的三份“账本”必须对齐

这是很多人踩坑的根源。分区信息其实存在三个地方,它们必须保持一致:

  1. 磁盘上的分区表:实际写在 mmcblk0 开头扇区里的数据
  2. 内核的分区视图:内核启动时读取分区表后,在内存里维护的设备列表(/proc/partitions)
  3. 文件系统的超级块:每个分区内部记录的文件系统大小

你改了磁盘分区表,内核不会自动知道,需要partprobe或partx通知它重读。你扩了分区,文件系统超级块里记录的大小还是旧的,需要resize2fs去更新。三层全部对齐,扩容才算真正完成。只做其中一步,就会出现“分区显示变大了但 df 还是老容量”或者“df 变了但重启后又回去了”这类诡异现象。

3. 分区扩容实操:从识别到生效的完整链路

3.1 操作前的信息采集与风险评估

动手之前,先把现状摸清楚。这一步花五分钟,能省掉后面五小时的救砖时间。

# 查看块设备树和挂载点 lsblk -f # 查看分区表详情 fdisk -l /dev/mmcblk0 # 查看当前文件系统使用情况 df -hT # 查看内核识别的分区 cat /proc/partitions

重点看几个信息:根分区是哪个设备(比如/dev/mmcblk0p2)、文件系统类型(ext4 还是 xfs)、分区起始扇区号、以及分区后面是否还有未分配空间。

注意:如果根分区后面紧跟着另一个分区,那你就不能直接扩根分区,得先处理后面那个分区。这种情况在嵌入式镜像里很常见——p1是 boot,p2是 rootfs,p3是 data,三个区把盘占满了。要扩p2,就得先删p3、扩p2、再重建p3,顺序错了数据就没了。

风险评估的核心问题就一个:你要动的分区里有没有不能丢的数据?如果是量产设备,建议先dd备份分区表:

dd if=/dev/mmcblk0 of=/tmp/mmcblk0_mbr_backup.img bs=512 count=1

这 512 字节就是 MBR 分区表,出问题了直接写回去就能恢复分区布局。GPT 的话要备份 34 个扇区(count=34)。

3.2 用 fdisk 调整分区表

假设场景是:/dev/mmcblk0有 8GB,p1是 boot(64MB),p2是 rootfs(3GB),后面有约 4.9GB 未分配。目标是把p2扩到占满剩余空间。

fdisk /dev/mmcblk0

进入交互界面后,操作序列如下:

  1. 输入p打印当前分区表,记下p2的 Start 扇区号(比如 133120)
  2. 输入d,然后输入2,删除分区 2
  3. 输入n,选择p(主分区),分区号输入2
  4. First sector 输入刚才记下的起始扇区号(133120),必须完全一致
  5. Last sector 直接回车,使用默认值(即磁盘末尾)
  6. 提示是否移除 ext4 签名时,选N(不移除)
  7. 输入p再次确认分区表,检查p2的 Start 没变、End 变大了
  8. 输入w写入并退出

这里最关键的是第 4 步的起始扇区必须和原来一模一样。因为文件系统的超级块就在分区起始位置,起始扇区变了,文件系统就找不到了。删除分区再重建,本质上只是改了分区表里的结束扇区号,分区内的数据一个字节都没动。

提示:fdisk删除分区时只是抹掉了分区表条目,不会擦除分区内的数据。所以“删了再建”这个操作在起始扇区不变的前提下是安全的。但如果你手抖把起始扇区改了,那就真的找不回来了。

3.3 通知内核重读分区表

写完分区表后,fdisk可能会提示“The kernel still uses the old table”。这时候需要手动通知内核:

partprobe /dev/mmcblk0

或者:

partx -u /dev/mmcblk0

如果这两个命令都报“device is busy”(根分区正在使用中,无法重读),那就需要重启。这是根分区扩容绕不过去的一步——内核没法在根分区挂载状态下重新扫描它的分区表。重启后lsblk应该能看到p2的 SIZE 变大了。

有些发行版支持partx -u对非根分区热更新,但根分区基本都得重启。我试过用blockdev --rereadpt,对根分区同样无效。所以实操流程就是:改分区表 → 重启 → 扩文件系统。

3.4 resize2fs 在线扩容文件系统

重启之后,分区变大了,但df -h看根分区还是 3GB。因为 ext4 文件系统的超级块里记录的大小没变。这时候resize2fs上场:

resize2fs /dev/mmcblk0p2

不带大小参数时,resize2fs会自动把文件系统扩展到分区设备的当前大小。执行过程会输出一系列 block 数变化,几秒到几十秒不等,取决于分区大小。

执行完再df -h,容量应该就对了。

如果是 xfs 文件系统,命令不一样:

xfs_growfs /

xfs 只能扩不能缩,而且必须挂载状态下操作,参数是挂载点而不是设备名。这一点和 ext4 的resize2fs区别很大,别搞混了。

3.5 参数计算:resize2fs 到底改了什么

理解resize2fs的原理,能帮你在出问题时快速定位。ext4 文件系统的布局大致是:

  • 超级块(Superblock):记录文件系统总块数、块大小、inode 数量等
  • 块组描述符(Group Descriptors):每个块组的元信息
  • inode 表:文件元数据
  • 数据块:实际文件内容

resize2fs做的事就是:读取分区设备的当前大小 → 计算能容纳多少块 → 更新超级块里的总块数 → 为新增加的块组初始化描述符和位图。

块大小的计算:假设块大小 4KB,分区从 3GB 扩到 8GB,新增 5GB 空间,对应 5×1024×1024÷4 = 1310720 个新块。resize2fs会把这些块纳入管理,并更新空闲块计数。

注意:resize2fs扩容前会做一次文件系统检查(fsck)的预检,如果文件系统有错误会拒绝执行。这时候先跑e2fsck -f /dev/mmcblk0p2修复,再 resize。根分区的话,e2fsck需要在单用户模式或者从其他介质启动后执行。

3.6 完整操作流程速查

把上面的步骤串起来,一个标准的根分区扩容流程是:

# 1. 备份分区表 dd if=/dev/mmcblk0 of=/tmp/mbr_backup.img bs=512 count=1 # 2. 查看现状 lsblk -f fdisk -l /dev/mmcblk0 # 3. 调整分区表(删p2、重建p2、起始扇区不变、结束扇区到盘尾) fdisk /dev/mmcblk0 # 4. 重启(根分区必须重启才能重读分区表) reboot # 5. 重启后确认分区已扩大 lsblk # 6. 扩展文件系统 resize2fs /dev/mmcblk0p2 # 7. 验证 df -hT

整个流程里,第 3 步的起始扇区核对和第 4 步的重启是两个关键节点。其他步骤出错了还能补救,这两步错了基本就是数据丢失。

4. 常见故障与排查技巧实录

4.1 分区表改了但重启后没生效

这种情况通常是分区表写入失败,或者写到了错误的设备上。排查思路:

  • fdisk -l /dev/mmcblk0确认分区表确实变了
  • 检查是不是有多个块设备,操作错了盘(比如系统实际从mmcblk1启动,你改了mmcblk0)
  • 某些 eMMC 有 boot0/boot1 分区,mmcblk0boot0和mmcblk0是不同设备,别搞混

还有一种可能是 U-Boot 在启动时覆盖了分区表。有些嵌入式方案里,U-Boot 会根据环境变量重新写分区表,你手动改的会被覆盖。这种情况要改 U-Boot 的启动脚本或者环境变量。

4.2 resize2fs 报 “Filesystem does not support online resizing”

ext4 支持在线扩容,但前提是内核编译时开启了CONFIG_EXT4_FS的 resize 支持。嵌入式内核为了精简体积,有时会裁掉这个功能。确认方法:

grep CONFIG_EXT4_FS_RESIZE /boot/config-$(uname -r)

如果没有这个配置项,或者值为n,那就只能离线扩容——从其他介质启动,或者把分区卸载后操作。根分区的话,用 Live USB 或者网络启动进入救援模式。

4.3 扩容后 df 显示容量没变

resize2fs执行成功但df没变化,通常是文件系统挂载时缓存了旧的超级块信息。尝试:

mount -o remount /

或者直接重启。如果重启后还是老容量,检查resize2fs的输出里有没有报错,以及dumpe2fs /dev/mmcblk0p2 | grep "Block count"看超级块里的块数是否真的变了。

4.4 分区号错乱导致挂载失败

MBR 的逻辑分区编号从 5 开始,如果你删除了扩展分区里的某个逻辑分区,后面的分区号可能会变。比如原来p5、p6、p7,删了p5之后,原来的p6可能变成p5。这会导致/etc/fstab里的挂载项失效。

解决办法:用 UUID 或 LABEL 挂载,不要用设备名。blkid可以查看分区的 UUID,/etc/fstab里写成UUID=xxxx-xxxx / ext4 defaults 0 1的形式,这样分区号变了也不影响挂载。

4.5 常见问题速查表

现象可能原因排查命令解决方式
分区表改了不生效内核未重读cat /proc/partitionspartprobe或重启
resize2fs 报错文件系统有错误e2fsck -f /dev/mmcblk0pX先修复再 resize
df 容量不变超级块未更新dumpe2fs -h /dev/mmcblk0pX重新 resize 或 remount
挂载失败分区号变了blkid改用 UUID 挂载
设备不识别驱动未加载`dmesggrep mmc`
扩容后数据丢失起始扇区改错无法直接排查从备份恢复分区表

4.6 几个保命技巧

第一,操作前一定备份分区表。512 字节的 MBR 备份成本几乎为零,但出问题时能救命。GPT 备份 34 个扇区,也就 17KB。

第二,起始扇区用笔抄下来。不要依赖记忆,fdisk里p命令打印出来后,把 Start 列的数字记在纸上或者另一个终端里。删分区重建时对照着输入。

第三,根分区扩容必须重启。不要试图用partx热更新根分区,内核不会允许。重启是最稳妥的路径。

第四,resize2fs 之前先 e2fsck。虽然 resize2fs 自己会做预检,但显式跑一次e2fsck -f能提前发现文件系统问题,避免 resize 到一半失败。

第五,嵌入式设备注意 U-Boot 的分区表覆盖。如果重启后分区表恢复原样,检查 U-Boot 环境变量里有没有partition相关的脚本。

4.7 一个真实的踩坑记录

之前处理过一块全志方案的板子,eMMC 16GB,出厂镜像只用了 4GB。我按标准流程fdisk删了p2重建,起始扇区核对无误,w写入,重启。结果重启后系统直接起不来了,串口打印停在 “Waiting for root device”。

排查了半天发现,这块板子的 U-Boot 里写死了 root 分区的结束扇区号,用来计算 rootfs 的校验和。我扩了分区,校验和对不上,U-Boot 拒绝启动。解决办法是进 U-Boot 命令行,更新环境变量里的分区参数,或者重新烧录一个不校验的 U-Boot。

这个坑的教训是:嵌入式设备的分区表可能被多个组件引用——内核、U-Boot、甚至 BootROM。改之前先确认启动链路上有没有硬编码的分区信息。通用 Linux 发行版没这个问题,但嵌入式方案里很常见。

4.8 国产化设备上的注意事项

国产 Linux 设备(比如基于飞腾、龙芯、兆芯的平台)在存储分区处理上,底层逻辑和通用 Linux 一致,但有几个差异点:

  • 分区工具版本可能较老:部分国产发行版自带的fdisk不支持 GPT,或者resize2fs版本偏低。建议先fdisk --version和resize2fs -V确认版本。
  • 文件系统可能是 ext4 的定制版:某些国产系统对 ext4 做了修改,resize2fs需要用配套版本,用通用版的可能报错。
  • 启动介质可能是 UFS 而非 eMMC:UFS 设备在 Linux 下命名可能是sd开头(走 SCSI 子系统),不是mmcblk。用lsblk确认实际设备名。

这些差异不影响操作流程,但工具选型和设备名确认上要多留个心眼。

5. 文件系统选型与长期维护建议

5.1 ext4 和 xfs 在扩容上的差异

嵌入式设备上 ext4 是绝对主流,但 xfs 在部分国产服务器和工作站上也有使用。两者在扩容操作上的核心差异:

操作ext4xfs
扩容命令resize2fsxfs_growfs
参数设备名(如 /dev/mmcblk0p2)挂载点(如 /)
在线扩容支持支持
缩小支持(离线)不支持
前置检查e2fsckxfs_repair(离线)

xfs 不支持缩小这一点很关键。如果你不确定未来会不会需要缩分区,选 ext4 更灵活。但 xfs 在大文件和高并发场景下性能更好,服务器场景可以考虑。

5.2 分区布局的长期规划建议

与其每次存储不够了再扩容,不如一开始就把分区布局规划好。几个原则:

  • 根分区不要占满整盘:留 10%-20% 的未分配空间,后续扩容不用动其他分区
  • 数据分区独立:把/var、/home、/data单独分区,根分区扩容不影响数据
  • boot 分区给足:内核更新频繁的话,/boot给 512MB 以上,避免内核装不下
  • 用 LVM 做弹性管理:如果设备支持,LVM 可以在线扩缩逻辑卷,比直接操作分区灵活得多

LVM 在嵌入式设备上用得少,因为增加了一层抽象,启动链路变复杂。但在国产化服务器和工作站上,LVM 是标配。lvextend+resize2fs的组合比fdisk删分区重建安全得多,因为不涉及分区表改动。

5.3 监控与预警

存储分区问题最好的处理方式是提前发现。几个实用的监控点:

# 分区使用率超过 80% 告警 df -h | awk 'NR>1 && int($5)>80 {print $0}' # inode 使用率检查(小文件多时容易耗尽) df -i | awk 'NR>1 && int($5)>80 {print $0}' # 查看块设备错误 dmesg | grep -i "mmc.*error\|I/O error"

嵌入式设备上,eMMC 的寿命有限,频繁写入会导致坏块。如果dmesg里出现大量 MMC 错误,说明 eMMC 可能快挂了,这时候扩容也没意义,得换硬件。

5.4 关于 resize2fs 的一个冷知识

resize2fs其实支持指定目标大小,比如resize2fs /dev/mmcblk0p2 6G。这个用法在分区比文件系统大、但你不想全用的时候有用。不过大多数场景下不带参数直接扩到分区满就行。

另外,resize2fs在扩容时会保留原有的 inode 数量。如果你的场景是小文件极多,扩容后 inode 可能不够用。这时候需要在 resize 时用-N参数调整 inode 数量,但操作更复杂,建议直接重建文件系统。

5.5 最后分享一个实用脚本

把根分区扩容的检查逻辑写成一个脚本,每次拿到新设备跑一遍,能快速判断是否需要扩容:

#!/bin/bash ROOT_DEV=$(findmnt -n -o SOURCE /) ROOT_DISK=$(lsblk -n -o PKNAME "$ROOT_DEV") DISK_SIZE=$(lsblk -n -o SIZE "/dev/$ROOT_DISK" | head -1) PART_SIZE=$(lsblk -n -o SIZE "$ROOT_DEV") FS_SIZE=$(df -h / | awk 'NR==2 {print $2}') echo "磁盘总容量: $DISK_SIZE" echo "根分区容量: $PART_SIZE" echo "文件系统容量: $FS_SIZE" # 检查分区后是否有未分配空间 LAST_PART_END=$(fdisk -l "/dev/$ROOT_DISK" | grep "^/dev" | tail -1 | awk '{print $3}') DISK_SECTORS=$(fdisk -l "/dev/$ROOT_DISK" | grep "sectors" | awk '{print $7}') if [ "$LAST_PART_END" -lt "$DISK_SECTORS" ]; then echo "警告: 分区后有未分配空间,可执行扩容" else echo "分区已占满磁盘,无需扩容" fi

这个脚本在fdisk -l输出格式不同的系统上可能需要微调,但思路是通用的:对比最后一个分区的结束扇区和磁盘总扇区数,有差值就说明有未分配空间。

存储分区处理这件事,说到底就是三层对齐——分区表、内核视图、文件系统超级块,三者一致了,容量就对了。操作本身不复杂,复杂的是嵌入式设备上各种非标准的启动约束和硬件差异。把底层逻辑吃透,遇到再奇怪的现象也能顺着链路排查下去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询