多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Linux eMMC存储分区扩容实战:从分区表到resize2fs

Linux eMMC存储分区扩容实战:从分区表到resize2fs 1. 从一个真实的存储告警说起那天下午产线上的测试机突然报磁盘空间不足我登上去一看df -h显示根分区只剩不到 200MB而这块 eMMC 明明标称 32GB。lsblk一敲问题一目了然mmcblk0总容量 29.1G但mmcblk0p2这个主分区只划了 4G后面一大截空间全是空的既没分区也没格式化。这种场景在嵌入式 Linux 项目里太常见了——厂商给的出厂镜像为了兼容小容量存储分区表往往做得很保守等你换了大容量 eMMC 或者想扩根分区时就得自己动手把这块闲置地利用起来。这篇内容就是围绕Linux 设备存储分区问题处理展开的核心聚焦在mmcblk0这类 eMMC/SD 卡设备上讲清楚分区表怎么看、空间怎么扩、resize2fs怎么用、踩坑了怎么救。适合三类人一是做嵌入式 Linux 的工程师天天跟板子和 eMMC 打交道二是运维同学遇到服务器磁盘分区不合理需要在线扩容三是刚接触 Linux 存储的初学者想搞明白分区表、文件系统、挂载这几层到底什么关系。我会尽量把底层原理讲透同时给出可以直接抄的命令让你看完就能上手。需要先说明一点分区操作是有风险的动作尤其是对已经在跑业务的生产设备。我下面讲的所有操作默认你已经在测试环境验证过或者至少做好了数据备份。分区表一旦写错轻则分区丢失重则整块盘的数据都读不出来这个心理准备要有。2. 先搞懂 Linux 存储的分层结构2.1 块设备、分区表、文件系统三层关系很多人一上来就敲fdisk但其实没搞清楚自己在操作哪一层。Linux 存储从下到上大致分三层我用一个生活化的类比帮你记住块设备层就是那块物理存储介质比如/dev/mmcblk0、/dev/sda、/dev/nvme0n1。它是一整块生地没有边界没有格式。分区表层在块设备上划格子把一整块地分成几块宅基地每块就是一个分区比如/dev/mmcblk0p1、/dev/mmcblk0p2。分区表记录的就是哪块地从哪到哪。文件系统层在每块宅基地上盖房子格式化成 ext4、f2fs、xfs 等然后挂载到目录树才能用。关键点在于分区表决定边界文件系统决定内容。你扩了分区表里的边界文件系统并不会自动跟着变大还得用resize2fs之类的工具去通知文件系统你的地盘变大了。这就是为什么扩容通常要两步走先改分区表再扩文件系统。mmcblk0这个命名也有讲究。mmc是 MultiMediaCard 的缩写blk表示 block device0是设备序号。后面的p1、p2是 partition 编号。注意SD 卡和 eMMC 在 Linux 里都走 mmc 子系统所以命名规则一样。而 SATA 盘是sdaNVMe 盘是nvme0n1分区命名分别是sda1和nvme0n1p1这个差异在写脚本时要注意别写死了。2.2 分区表类型MBR 与 GPT 的取舍分区表主要有两种MBRMaster Boot Record和GPTGUID Partition Table。嵌入式设备上 MBR 更常见因为它简单、兼容性好很多 SoC 的 boot ROM 只认 MBR。但 MBR 有个硬伤最多 4 个主分区单盘最大支持 2TB。GPT 则支持 128 个分区、单盘 9.4ZB还自带分区表备份和 CRC 校验。怎么判断你手上是哪种用fdisk -l看输出如果开头写着Disklabel type: dos就是 MBR写着gpt就是 GPT。或者用parted -l更直观。对比项MBRGPT最大分区数4 主分区或 3 主 扩展128默认最大磁盘容量2TB9.4ZB分区表备份无有头尾各一份校验机制无CRC32嵌入式兼容性极好一般部分 boot ROM 不支持嵌入式项目里如果存储小于 2TB 且分区数不超过 4 个MBR 完全够用没必要折腾 GPT。但如果你要做 OTA 双系统、恢复分区、数据分区加起来超过 4 个那就得考虑 GPT 或者用扩展分区。我个人的经验是新项目如果 SoC 支持优先 GPT因为分区表有备份掉电损坏的概率低很多这在工业现场很重要。2.3 为什么出厂镜像总留一大截空间回到开头那个问题为什么 32G 的 eMMC 只用了 4G原因通常有三个。第一镜像通用性厂商一套镜像要适配 8G、16G、32G 多种容量的板子只能按最小的来划剩下的留白。第二升级预留有些方案故意留空间给后续 OTA 或者数据分区扩展。第三历史遗留早期方案就是这么定的后面没人改。这块留白空间如果不处理就是纯浪费。处理方式有两种一是新建分区把空闲空间单独划出来挂到/data或/opt二是扩展现有分区直接把根分区撑大。选哪种取决于你的需求如果根分区本来就紧张直接扩根最省事如果想把系统和数据分离那就新建分区。3. 动手前的侦察把现状摸清楚3.1 用 lsblk 和 fdisk 看清全貌动手之前先把现状摸清楚这一步千万别省。我习惯先敲lsblk它用树状结构展示块设备和分区一眼就能看出哪块盘有多大、分了几个区、挂在哪。lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT输出大概长这样NAME SIZE TYPE FSTYPE MOUNTPOINT mmcblk0 29.1G disk ├─mmcblk0p1 128M part vfat /boot ├─mmcblk0p2 4G part ext4 / └─mmcblk0p3 512M part ext4 /opt看到没mmcblk0总共 29.1G但三个分区加起来才 4.6G 左右中间和后面有大片空白。这时候再用fdisk -l /dev/mmcblk0看分区表的精确边界fdisk -l /dev/mmcblk0重点看Start和End两列单位是扇区sector默认 512 字节。比如mmcblk0p2从扇区 526336 到 8914943算一下就是 (8914943 - 526336 1) × 512 ÷ 1024 ÷ 1024 ≈ 4G。而整块盘的最后一个扇区大概是 61071359中间那一大段就是空闲的。提示fdisk -l里的Start是分区起始扇区改分区时这个值绝对不能动动了分区里的数据就废了。你只能改End而且新End不能超过磁盘总扇区数。3.2 确认文件系统类型和挂载状态分区表看完了还得确认文件系统类型因为不同文件系统的扩容工具不一样。ext4 用resize2fsxfs 用xfs_growfsf2fs 用resize.f2fs。用df -T或者blkid都能看df -T blkid /dev/mmcblk0p2blkid的输出会明确告诉你TYPEext4还是别的。同时确认挂载点因为在线扩容和离线扩容的操作方式不同。根分区通常没法卸载只能在线扩数据分区可以先umount再离线扩更安全。还有一个容易被忽略的点检查分区是否被 LVM 或加密层包着。如果lsblk输出里分区下面还有lvm或crypt类型那扩容就得多一层操作先扩物理卷再扩逻辑卷。嵌入式设备一般不用 LVM但服务器上很常见别搞混了。3.3 备份分区表五分钟换一条命这一步我要单独拎出来强调。改分区表之前务必先备份。备份命令很简单sfdisk -d /dev/mmcblk0 mmcblk0_partition_backup.txt或者用dd把分区表那一段前 512 字节的 MBR或者 GPT 的头尾各 34 个扇区dump 出来dd if/dev/mmcblk0 ofmbr_backup.bin bs512 count1别小看这个文件我见过太多人手一抖把分区表写坏整块盘读不出来最后靠备份恢复的。备份文件存到别的设备上别存在同一块盘里不然盘挂了备份也没了。这个习惯养成之后你操作起来心里就有底不会手抖。4. 扩容实战从分区表到文件系统4.1 方案选型扩根分区还是新建分区前面说了两种思路这里展开讲讲怎么选。扩根分区的优点是简单一条命令搞定不用改挂载配置缺点是系统和数据混在一起将来重刷系统数据会丢。新建分区的优点是系统数据分离重刷系统不影响数据缺点是要改/etc/fstab多一步配置。我的建议是如果是临时救急或者测试设备直接扩根如果是量产设备或者有数据持久化需求新建数据分区。下面两种我都讲你按需取用。4.2 扩展现有分区fdisk 删除重建法ext4 分区扩容有个经典操作用fdisk把分区删掉再重建但只改结束扇区起始扇区保持不变。这样分区表里的边界变大了但分区里的数据其实没动因为起始位置没变。这个操作听起来吓人其实很安全前提是你起始扇区别改错。具体步骤以扩展mmcblk0p2为例# 1. 先备份分区表 sfdisk -d /dev/mmcblk0 /tmp/part_backup.txt # 2. 进入 fdisk 交互 fdisk /dev/mmcblk0进去之后按顺序操作输入p打印当前分区表记下 mmcblk0p2 的 Start 扇区号比如 526336。输入d然后选2删除分区 2。别慌这只是删分区表记录数据还在。输入n选p主分区分区号选2。First sector 一定要填刚才记下的 526336千万别直接回车用默认值默认值可能不是原来的起始位置。Last sector 直接回车用默认即磁盘末尾或者填一个你想要的大小。如果提示Partition #2 contains a ext4 signature之类的选N不要删除签名。输入p再确认一遍新分区 2 的 Start 和原来一致End 变大了。确认无误后输入w写入。写完分区表后内核可能还没重新读取需要刷新一下partprobe /dev/mmcblk0或者partx -u /dev/mmcblk0。如果提示设备忙重启一下也行但生产环境尽量别重启。注意fdisk删除重建时如果分区号变了或者起始扇区变了数据就找不回来了。所以每一步都要p确认。我一般会开两个终端一个操作一个随时fdisk -l对照。4.3 resize2fs让文件系统跟上分区边界分区表改完lsblk看mmcblk0p2已经变成 28G 了但df -h还是显示 4G。这是因为文件系统还不知道自己地盘变大了。这时候就轮到resize2fs出场# 在线扩容根分区也能用 resize2fs /dev/mmcblk0p2如果文件系统是挂载状态resize2fs会自动做在线扩容ext4 支持这个特性。执行完再df -h应该就能看到根分区变成 28G 了。整个过程通常几秒钟数据不会丢。如果提示The filesystem is already XXX blocks long. Nothing to do!说明分区表没改成功回去检查fdisk -l的 End 扇区。如果提示Filesystem does not support online resize那就得先umount再离线扩扩完再挂回来。对于 xfs 文件系统命令换成xfs_growfs /注意 xfs 只能扩不能缩而且必须挂载状态下扩。f2fs 则是resize.f2fs /dev/mmcblk0p24.4 新建数据分区把空闲空间利用起来如果你选择新建分区流程是这样的。假设空闲空间在mmcblk0p2之后你想划一个mmcblk0p3出来fdisk /dev/mmcblk0 # n - p - 3 - 起始扇区回车用默认 - 结束扇区回车用默认 - w partprobe /dev/mmcblk0 mkfs.ext4 /dev/mmcblk0p3 mkdir -p /data mount /dev/mmcblk0p3 /data然后写入/etc/fstab实现开机自动挂载。这里有个坑fstab 里最好用 UUID 而不是设备名因为设备名可能变比如插了别的存储UUID 是文件系统唯一标识更稳。用blkid /dev/mmcblk0p3拿到 UUID然后写UUIDxxxx-xxxx-xxxx /data ext4 defaults,noatime 0 2noatime是个实用选项表示不记录文件访问时间能减少写入、延长 eMMC 寿命嵌入式设备强烈建议加上。5. 那些年踩过的坑与排查实录5.1 分区表写了但内核不认最常见的问题fdisk里w写入了但lsblk看还是老样子。这是因为内核缓存了旧的分区表。解决办法按顺序试partprobe /dev/mmcblk0 partx -u /dev/mmcblk0 blockdev --rereadpt /dev/mmcblk0如果都提示Device or resource busy说明有分区正在挂载使用内核不让重读。这时候要么umount相关分区要么只能重启。根分区在用的场景下partprobe有时也会失败但resize2fs依然能工作因为它是直接跟块设备对话的不完全依赖内核分区表缓存。这个细节很多人不知道实测有效。5.2 resize2fs 报错 Nothing to do这个报错基本就是分区表没扩成功。排查顺序先fdisk -l看 End 扇区是不是真的变大了再看cat /proc/partitions里内核认到的分区大小两者不一致就是内核没刷新回到上一条处理。还有一种可能是你扩的分区和文件系统对不上比如扩了mmcblk0p2却对mmcblk0p3执行resize2fs这种低级错误新手常犯敲命令前多看一眼设备名。5.3 扩容后系统起不来这个最吓人通常是分区表改坏了。表现是开机卡在 bootloader 或者 kernel panic 找不到根文件系统。原因多半是起始扇区改错了或者分区号变了导致 bootloader 的启动参数对不上。这时候别慌用备份恢复sfdisk /dev/mmcblk0 /tmp/part_backup.txt如果盘都进不去系统就把 eMMC 拆下来用读卡器接到另一台机器上恢复。所以再次强调备份分区表是保命操作。5.4 常见问题速查表现象可能原因解决方向lsblk 看不到新分区内核未刷新分区表partprobe / partx -u / 重启resize2fs 提示 Nothing to do分区表未扩或设备名错检查 fdisk -l 的 End 扇区扩容后 df 没变化文件系统未扩执行 resize2fs开机找不到根分区分区表起始扇区被改用备份恢复分区表fstab 挂载失败进紧急模式UUID 写错或分区不存在检查 blkid 输出修正 fstabeMMC 写入寿命告警频繁写日志挂载加 noatime日志改 tmpfs5.5 几条用血换来的经验第一操作前一定sfdisk -d备份这个动作花不了十秒但能救命。第二fdisk 删除重建时起始扇区用笔记下来别靠记忆人一紧张就记错。第三生产设备尽量别在线扩根分区虽然 ext4 支持但万一掉电分区表和文件系统状态不一致恢复起来很麻烦。第四扩完记得验证df -h看容量、mount看挂载、重启一次看能不能正常起来三步都过了才算完。第五eMMC 和 SD 卡的操作逻辑一样但 eMMC 是焊死的操作失误没法换卡所以对 eMMC 要更谨慎。6. 自动化脚本与长期维护建议6.1 写一个安全的扩容脚本如果你经常要处理这类问题可以写个脚本把流程固化下来减少手抖。核心思路是先检测空闲空间再自动计算新分区边界最后执行扩容。下面是个简化版示例重点看逻辑#!/bin/bash DEV/dev/mmcblk0 PART${DEV}p2 # 备份分区表 sfdisk -d $DEV /tmp/part_backup_$(date %s).txt # 获取分区起始扇区和磁盘总扇区 START$(fdisk -l $DEV | grep ${PART} | awk {print $2}) TOTAL$(fdisk -l $DEV | grep Disk $DEV | awk {print $7}) echo 分区起始扇区: $START, 磁盘总扇区: $TOTAL # 这里用 sfdisk 非交互式重建分区只改 size # 实际脚本要更严谨建议先 dry-run这个脚本只是骨架实际用的时候要加大量校验确认设备存在、确认分区号、确认起始扇区、确认没有其他进程占用。我个人的做法是脚本只做检测和提示最后一步写入让工程师手动确认避免自动化误操作。6.2 长期维护分区规划的前瞻性与其事后扩容不如一开始就规划好。新项目定分区方案时我一般这么分/boot给 256M 到 512M/给 4G 到 8G剩下的全给/data。这样系统分区够用又不浪费数据分区大将来存日志、数据库、用户文件都够。如果做双系统 OTA再单独划两个 rootfs 分区和一个 recovery 分区。另外定期检查磁盘使用率别等满了才处理。可以写个 cron 任务超过 80% 就发告警。嵌入式设备上还可以把日志目录挂到 tmpfs减少 eMMC 写入延长寿命。这些习惯看起来小但能帮你省掉很多半夜被叫起来处理故障的麻烦。6.3 关于文件系统选择的补充最后提一句文件系统选型。ext4 最通用工具链最全resize2fs在线扩容成熟是默认选择。f2fs 针对闪存优化随机写性能好适合 eMMC但扩容工具没 ext4 那么普及。xfs 适合大文件和高并发但只能扩不能缩。嵌入式设备上ext4 和 f2fs 是主流选哪个看你的读写模式日志类、频繁小文件写入选 f2fs通用场景选 ext4。选定了就别轻易换因为换文件系统意味着重新格式化数据得迁移成本很高。我在实际项目里踩过的最大一个坑是有次给客户设备扩根分区图省事没备份分区表结果 fdisk 里起始扇区手滑填错整块 eMMC 的分区全乱最后只能返厂重刷。从那以后我养成了一个习惯任何分区操作前先sfdisk -d备份再开一个终端watch -n 1 fdisk -l /dev/mmcblk0实时盯着这样每一步变化都看得见心里踏实。这个习惯分享给你希望你别走我走过的弯路。
返回列表