1. 项目概述:为什么一张SD卡的分区配置值得花20分钟认真对待
你手头有一张32GB的SD卡,准备刷入某个嵌入式系统镜像,或者想把它变成树莓派的启动盘,又或者只是想当个高速移动U盘用——结果插进电脑,系统只识别出几百MB,剩下的空间灰掉不可用;或者格式化完发现写入速度慢得像在等泡面;再或者烧录完系统,设备根本无法启动。这些问题90%以上不是SD卡坏了,而是分区表没配对、文件系统选错、起始扇区没对齐。而解决它们最底层、最可靠、最不依赖图形界面的工具,就是fdisk——Linux下那个看起来古板、命令行里一串字母加数字、连帮助都要敲m才能看到的磁盘分区神器。
我做过不下50个嵌入式项目,从工业PLC数据采集模块到教育机器人主控板,所有需要外置存储的场景,第一道关永远是SD卡初始化。很多新手一上来就双击GUI磁盘工具点“恢复”“擦除”“重新分区”,结果要么破坏了厂商预置的引导区,要么把分区起始位置设在了4KB不对齐的扇区上,导致后续IO性能腰斩。而fdisk之所以至今仍是嵌入式开发者的首选,正因为它强制你直面每一个关键参数:MBR还是GPT?主分区还是扩展分区?起始扇区号是多少?文件系统类型留白还是立刻格式化?它不替你做决定,但每一步都给你解释权和控制权。这篇文章不讲“fdisk怎么用”的基础语法,而是聚焦一个真实高频场景:如何用fdisk精准配置一张SD卡的分区大小与格式,确保它既满足启动要求(如boot分区必须FAT32且位于前1GB),又兼顾数据区读写效率(如ext4对齐+预留inode)。适合刚接触嵌入式开发的硬件工程师、树莓派/香橙派玩家、以及需要批量部署SD卡的运维人员。你不需要会C语言,但得愿意敲几行命令、看懂扇区编号、理解“对齐”不是玄学而是物理事实。
2. 分区设计逻辑与方案选型:为什么不能直接“全盘格式化”
2.1 SD卡的物理结构决定了分区必须“讲规矩”
SD卡不是一块均匀的硬盘,它的内部由NAND闪存颗粒组成,每个颗粒按“块(Block)→页(Page)→字节(Byte)”三级组织。典型消费级SD卡的擦除最小单位是块(Block),大小通常为128KB~2MB;而读写最小单位是页(Page),常见512B或4KB。如果分区起始位置没有对齐到块边界,一次4KB写入可能跨两个物理块,触发“读-改-写”流程——先读出整个块,修改其中4KB,再擦除原块,最后写入新块。这不仅拖慢速度,更大幅缩短SD卡寿命。fdisk默认创建分区时,起始扇区会自动对齐到2048扇区(即1MB边界),正是为了匹配主流NAND块大小。但如果你手动输入起始扇区号为1024(512KB),哪怕系统能识别,实测连续写入速度可能下降40%以上。
提示:用
sudo fdisk -l /dev/sdX查看当前分区起始扇区号,确认是否为2048的整数倍。不是?别急着删,先记下这个值,后面重分区时要对齐。
2.2 启动型SD卡必须分至少两个区:boot + rootfs
绝大多数ARM架构嵌入式设备(如树莓派、全志H3/H5平台)采用两级启动:第一阶段由SoC内置ROM加载SD卡第一个分区(FAT32格式)中的bootcode.bin或u-boot-spl.bin;第二阶段再由该引导程序加载第二个分区(通常是ext4)里的内核镜像和根文件系统。这意味着:
- boot分区必须是FAT32:因为SoC ROM固件只支持FAT16/FAT32,不认NTFS或ext系列;
- boot分区必须位于物理磁盘最前端:起始扇区号必须≤2048,否则ROM无法定位;
- boot分区不宜过大:32MB~256MB足够存放所有引导文件(含Device Tree、initramfs),太大反而增加启动扫描时间;
- rootfs分区建议ext4而非ext3:ext4支持日志、延迟分配、更大的单文件上限,且对SD卡的TRIM指令支持更成熟(需配合
mount -o discard)。
我曾遇到一个案例:某款国产工控主板要求boot分区严格限定在前1GB内,且必须是FAT32。用户用Windows磁盘管理工具创建了一个1GB的NTFS分区,结果板子死活不亮——不是固件问题,是文件系统不兼容。后来用fdisk重建MBR分区表,手动指定boot分区为FAT32、起始扇区2048、大小128MB,问题当场解决。
2.3 方案选型:MBR vs GPT?主分区 vs 逻辑分区?
| 对比项 | MBR(msdos) | GPT |
|---|---|---|
| 最大支持容量 | 2TB | 理论无上限(实际受限于内核) |
| 分区数量 | 最多4个主分区,或3主+1扩展(扩展内可建逻辑分区) | 最多128个主分区(UEFI规范) |
| SD卡适用性 | ✅ 绝大多数嵌入式设备ROM仅支持MBR | ❌ 树莓派4B及以下、全志平台等均不识别GPT boot |
| 容错性 | 仅一份分区表,损坏即丢失全部分区信息 | 主备份两份分区表,校验和保护 |
结论很明确:除非你用的是x86_64架构的迷你PC(如Intel NUC)跑UEFI系统,否则SD卡一律选MBR。GPT在SD卡上不仅是“不必要”,更是“不兼容”。至于分区类型,boot分区必须是主分区(Primary),因为ROM只扫描前4个主分区;rootfs分区可以是主分区(推荐),也可以是逻辑分区(若需更多分区),但逻辑分区会增加一层映射开销,对性能无益。
注意:
fdisk默认创建MBR分区表,无需额外指定。输入g命令才会切到GPT模式——千万别手滑。
3. fdisk核心操作详解:从识别设备到写入分区表
3.1 第一步:准确识别SD卡设备名,避免误操作
插上SD卡后,不要凭经验猜/dev/sdb——USB接口、读卡器芯片、内核加载顺序都会影响设备命名。正确做法是:
# 插卡前执行一次,记录当前设备列表 ls /dev/sd* # 插卡后再执行,对比新增设备 ls /dev/sd*更稳妥的方式是结合dmesg:
sudo dmesg | tail -20输出中会显示类似:
[12345.678901] sd 2:0:0:0: [sdc] 62333952 512-byte logical blocks: (31.9 GB/29.7 GiB) [12345.678923] sd 2:0:0:0: [sdc] Write Protect is off [12345.678934] sd 2:0:0:0: [sdc] Mode Sense: 03 00 00 00这里[sdc]就是你的SD卡设备名。务必确认是sdc而非sdb(可能是你另一块移动硬盘)。一旦选错,fdisk的w命令会直接覆写错误设备的分区表,数据将不可逆丢失。
实操心得:我习惯在终端标题栏写上当前操作设备,比如
[ROOT@HOST] /dev/sdc —— SD CARD FOR RPI,避免切换窗口时混淆。另外,首次操作前必先卸载所有挂载点:sudo umount /dev/sdc*,否则fdisk会提示“device busy”。
3.2 第二步:进入fdisk交互界面,删除旧分区
sudo fdisk /dev/sdc进入后先输入p打印当前分区表,确认是否真有旧分区。如果有,输入d删除。注意:d默认删第一个分区,若有多分区,需连续按d直到提示“No partition is defined yet”或显示“Partition number (1-4)”时,输入对应编号(如d→1删boot,d→2删rootfs)。
关键细节:
fdisk的删除操作只是修改内存中的分区表副本,未执行w前一切可撤销。此时输入q退出即放弃所有更改。这是新手最大的安全缓冲带——大胆试,错了重来。
3.3 第三步:创建boot分区(FAT32,主分区,128MB)
输入n新建分区 →p选主分区 →1设为第一个分区 → 起始扇区直接回车(默认2048,已对齐)→ 结束扇区输入+128M。这里+128M表示“从起始位置往后128MB”,fdisk会自动换算成扇区号(如128×1024×1024÷512=262144扇区)。完成后输入t修改分区类型,1选第一个分区,然后输入c(FAT32 LBA),这是MBR下FAT32的标准类型码。
为什么不是
b(FAT32)?b是旧版FAT32,不支持LBA大容量寻址;c才是现代SD卡必须的类型码。输错会导致某些老设备无法识别boot分区。
3.4 第四步:创建rootfs分区(ext4,主分区,占满剩余空间)
再次输入n→p→2→ 起始扇区回车(fdisk会自动设为boot分区结束后的下一个扇区,保证无缝衔接)→ 结束扇区直接回车(用光所有剩余空间)。此时输入p检查:应看到两个分区,/dev/sdc1约128MB,/dev/sdc2约31.8GB,起始扇区分别为2048和264192(2048+262144),完全对齐。
实测验证:用
sudo blockdev --getss /dev/sdc确认扇区大小(通常512B),再用sudo fdisk -l /dev/sdc查Start列数值,除以2048,结果必须为整数。不是?说明对齐失败,需删掉重来。
3.5 第五步:写入分区表并格式化
输入w保存并退出。此时内核会重读分区表,但旧挂载点仍存在,需手动刷新:
sudo partprobe /dev/sdc # 或重启udev服务(更彻底) sudo systemctl restart udisks2接着格式化:
# 格式化boot分区为FAT32 sudo mkfs.fat -F32 -n "BOOT" /dev/sdc1 # 格式化rootfs分区为ext4,预留5%空间给root(防满盘)、启用dir_index加速目录查找 sudo mkfs.ext4 -L "ROOTFS" -m 5 -O dir_index /dev/sdc2参数说明:
-F32:强制FAT32,避免mkfs自动选FAT16;-n "BOOT":设置卷标,方便后续挂载识别;-m 5:预留5%空间给root用户,防止普通用户写满导致系统崩溃(SD卡尤其重要);-O dir_index:启用哈希索引目录,百万级文件下ls速度提升10倍以上。
注意:
mkfs.ext4耗时较长(32GB卡约3~5分钟),期间不要中断。若提示“discarding device blocks”,说明内核正在发送TRIM指令,这是好事——告诉SD卡哪些块已无效,可提前擦除,延长寿命。
4. 格式化后必做的三件事:验证、挂载、写入引导文件
4.1 验证分区结构与文件系统健康度
格式化完成后,立即执行双重验证:
# 1. 检查分区表是否写入成功 sudo fdisk -l /dev/sdc | grep -A5 "Device" # 应输出类似: # Device Boot Start End Sectors Size Id Type # /dev/sdc1 * 2048 264191 262144 128M c W95 FAT32 (LBA) # /dev/sdc2 264192 62333951 62069760 29.6G 83 Linux # 2. 检查FAT32分区是否可读 sudo fsck.fat -v /dev/sdc1 # 3. 检查ext4分区是否可读 sudo e2fsck -f /dev/sdc2e2fsck -f强制检查,即使文件系统标记为“clean”也执行。若提示“Superblock checksum does not match”,说明格式化异常,需重新mkfs.ext4。
4.2 挂载分区并设置合理挂载选项
创建挂载点:
sudo mkdir -p /mnt/boot /mnt/rootfs挂载时必须指定选项,否则默认挂载可能禁用关键特性:
# FAT32分区:启用sync写入(防断电丢引导文件)、禁止长文件名缓存(减少IO) sudo mount -t vfat -o sync,noatime,fmask=0022,dmask=0022 /dev/sdc1 /mnt/boot # ext4分区:启用discard(TRIM)、禁用访问时间更新(减少写入)、启用data=writeback(平衡性能与安全性) sudo mount -t ext4 -o discard,noatime,data=writeback /dev/sdc2 /mnt/rootfs选项解析:
sync:FAT32下强制同步写入,确保config.txt等关键引导文件不因断电丢失;noatime:禁用访问时间更新,SD卡每读一次就写一次atime,寿命直降;fmask/dmask:设置文件/目录默认权限掩码,0022即文件644、目录755,符合Linux标准;discard:挂载时即发送TRIM,比定期fstrim更及时;data=writeback:日志只记录元数据,不记录文件内容,写入速度比ordered快30%,对SD卡足够安全(因ext4日志本身已校验)。
实操心得:我曾因忘记
noatime,导致一块32GB SD卡在连续日志写入72小时后IO错误频发。加上该选项后,同型号卡稳定运行超18个月。
4.3 写入引导文件:boot分区不是空的,它必须有“心跳”
仅分区格式化还不够,boot分区必须包含设备能识别的引导文件。以树莓派为例:
# 下载最新固件(此处用官方链接示意,实际请查rpf.io) wget https://github.com/raspberrypi/firmware/archive/refs/heads/master.zip unzip master.zip cp -r firmware-master/boot/* /mnt/boot/ # 创建基础配置 echo "arm_64bit=1" | sudo tee /mnt/boot/config.txt echo "kernel=kernel8.img" | sudo tee -a /mnt/boot/config.txt关键文件清单(缺一不可):
bootcode.bin:树莓派GPU一级引导程序;start.elf:GPU二级引导,加载config.txt;config.txt:核心配置,定义分辨率、内存分配、overclock等;kernel8.img:64位内核镜像(ARMv8);bcm2711-rpi-4-b.dtb:设备树,描述硬件资源。
提示:不同平台引导文件名不同。全志H3平台需
sunxi-spl.bin+u-boot.bin;瑞芯微RK3399需idbloader.img+uboot.img。务必查阅对应SoC的Bootloader文档,不能直接复制树莓派文件。
5. 常见问题排查与避坑指南:那些让你抓狂的“小问题”
5.1 问题速查表:症状、原因、解决方案
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
fdisk提示“Device or resource busy” | 分区被挂载或有进程占用 | sudo lsof /dev/sdc*查占用进程,sudo umount /dev/sdc*卸载 |
格式化后lsblk显示分区但df -h不显示 | 内核未重读分区表 | sudo partprobe /dev/sdc或sudo blockdev --rereadpt /dev/sdc |
boot分区挂载后ls /mnt/boot为空 | FAT32卷标冲突或文件系统损坏 | sudo fsck.fat -a /dev/sdc1自动修复;或重新mkfs.fat -F32 |
rootfs分区df -h显示容量远小于预期 | ext4未启用64bit特性,无法使用大容量 | sudo tune2fs -O 64bit /dev/sdc2(需先e2fsck -f) |
| 设备启动卡在黑屏/LED常亮 | boot分区无bootcode.bin或config.txt语法错误 | 检查/mnt/boot/下是否存在bootcode.bin;用sudo nano /mnt/boot/config.txt确认无中文标点 |
5.2 避坑实战:三个血泪教训
坑一:用dd直接写入镜像后,再用fdisk调整分区大小——必然失败
很多教程教“先dd烧录官方镜像,再fdisk扩容rootfs”。但官方镜像(如Raspberry Pi OS)的rootfs分区是固定大小的,fdisk只能修改分区表,不会自动扩展文件系统。你看到/dev/sdc2变大了,但df -h仍显示原大小。正确做法是:fdisk调完分区后,必须执行sudo resize2fs /dev/sdc2,让ext4文件系统感知新空间。否则所有新增空间都是“黑洞”。
坑二:在Windows下用DiskGenius调整SD卡分区,导致Linux无法识别
Windows工具常将分区类型码设为07(NTFS)或0C(FAT32 LBA),而Linux内核期望c(FAT32 LBA)或83(Linux)。fdisk里看到Id列是07,mount就会报“unknown filesystem type 'ntfs'”。解决方案:sudo fdisk /dev/sdc→t→1→c→w,强制修正类型码。
坑三:SD卡插在USB3.0读卡器上,fdisk显示容量只有8GB
USB3.0读卡器芯片(如ASM1083)存在固件bug,会错误报告SD卡容量。换用USB2.0读卡器或直接插主板SD卡槽,fdisk -l立即显示真实容量。这不是fdisk问题,是硬件兼容性问题——别跟读卡器较劲,换一个。
5.3 性能优化终极技巧:让SD卡跑出10MB/s持续写入
默认mkfs.ext4参数对SD卡不够友好。实测提升写入速度的关键三步:
- 调整块大小:
sudo mkfs.ext4 -b 4096 -L "ROOTFS" /dev/sdc2(-b 4096设块大小为4KB,匹配SD卡页大小); - 禁用日志(仅限只读场景):
sudo tune2fs -O ^has_journal /dev/sdc2(移除日志,写入快50%,但断电风险高,仅用于数据采集等只读应用); - 挂载时启用
commit=60:sudo mount -t ext4 -o commit=60,discard,noatime /dev/sdc2 /mnt/rootfs(日志提交间隔60秒,减少频繁刷盘)。
我在某环境监测项目中,用上述组合将SD卡持续写入速度从3.2MB/s提升至10.7MB/s,满足每秒写入100条传感器数据的需求。但必须强调:禁用日志仅适用于可接受断电丢数据的场景,生产系统慎用。
6. 批量部署与自动化脚本:一个人管100张卡的正确姿势
当你要给实验室20台树莓派、产线50个工控盒同时配SD卡时,手动敲fdisk显然不现实。我用以下Bash脚本实现全自动分区+格式化:
#!/bin/bash # sdcard_setup.sh /dev/sdc "RPI_BOOT" "RPI_ROOT" DEVICE=$1 BOOT_LABEL=$2 ROOT_LABEL=$3 # 卸载所有分区 sudo umount ${DEVICE}* 2>/dev/null # 清空分区表(MBR) sudo dd if=/dev/zero of=${DEVICE} bs=512 count=1 # 使用fdisk非交互模式创建分区 sudo fdisk ${DEVICE} <<EOF n p 1 2048 +128M t c n p 2 w EOF # 刷新分区 sudo partprobe ${DEVICE} # 格式化 sudo mkfs.fat -F32 -n "${BOOT_LABEL}" ${DEVICE}1 sudo mkfs.ext4 -L "${ROOT_LABEL}" -m 5 -O dir_index ${DEVICE}2 echo "✅ SD卡 ${DEVICE} 分区完成:boot=${BOOT_LABEL}, rootfs=${ROOT_LABEL}"使用方法:
chmod +x sdcard_setup.sh sudo ./sdcard_setup.sh /dev/sdc "RPI_BOOT" "RPI_ROOT"脚本核心优势:
<<EOF语法实现fdisk非交互操作,避免人工输入失误;dd if=/dev/zero清空MBR,比fdisk d更彻底,杜绝旧分区残留;- 参数化
BOOT_LABEL和ROOT_LABEL,适配不同项目命名规范; - 末尾
echo提供清晰完成提示,便于集成到CI/CD流水线。
个人经验:在某次教育机器人集训中,我用此脚本+USB3.0多卡读卡器(支持4卡并行),15分钟完成60张SD卡初始化,错误率为0。而手动操作,每人每卡平均耗时8分钟,且3人中有1人因输错扇区号导致3张卡报废。
7. 结语:分区不是终点,而是系统稳定的起点
写完这篇,我顺手重测了一张用了两年的SD卡——sudo smartctl -a /dev/sdc显示Media_Wearout_Indicator值为87(满值100),UDMA_CRC_Error_Count为0,证明当初用fdisk对齐分区、mkfs.ext4启用dir_index、挂载加noatime的决策,实实在在延长了它的服役周期。技术文档里常把fdisk归为“基础命令”,但真正把它用透的人,往往也是那些系统最稳、故障率最低的团队。因为分区这件事,表面是划几道线,背后是物理层、驱动层、文件系统层的精密协同。你敲下的每一个回车,都在和NAND闪存的电子漂移对话。下次当你面对一张空白SD卡,别急着点“格式化”,先打开终端,输入sudo fdisk -l看看它的真实模样——那串数字背后,藏着比GUI界面上的进度条更真实的硬件世界。