1. 为什么今天还要学 fdisk?——一个被低估却不可替代的分区基石工具
你可能已经用过lsblk看磁盘布局,用过parted做 GPT 分区,甚至在图形界面里点几下就完成了分区操作。但只要你在 Linux 下真正做过服务器部署、嵌入式系统烧录、数据恢复现场响应,或者调试过一块新到的 NVMe SSD 在 CentOS 7 上识别异常的问题,你就一定会回到那个黑底白字、没有鼠标、只有光标闪烁的fdisk界面。它不炫酷,不自动,不带进度条,但它像一把瑞士军刀里的主刀片——小,硬,准,且从不撒谎。
fdisk 的核心价值,从来不是“多好用”,而是“多可靠”。它直接与内核的块设备层对话,绕过所有抽象层和缓存逻辑,所见即所得。当你执行fdisk /dev/sdb,它读取的是磁盘 MBR 或 DOS 兼容区域的真实扇区数据;当你敲下w写入,它调用的是ioctl(BLKRRPART)系统调用,强制内核重新读取分区表——这个动作,比任何 GUI 工具点击“应用”都更底层、更确定。这也是为什么在 Kali Linux 渗透测试中,当目标主机磁盘出现分区表损坏时,fdisk -l是第一个被调用的诊断命令;为什么在国产 Linux 发行版(如统信 UOS、麒麟 Kylin)的安装镜像里,即使提供了图形化分区器,其底层仍默认调用fdisk做兼容性兜底;为什么面试官问“如何安全清空一块 SSD 的分区信息”,答案永远是fdisk /dev/nvme0n1→d(全删)→w,而不是rm -rf /dev/nvme0n1p*——后者只是删设备节点,分区表还在盘上,重启后又会复活。
我做过三年一线运维,处理过 200+ 台物理服务器的磁盘初始化。最常遇到的场景不是“不会分”,而是“分错了不敢动”。比如某次给一台 Dell R730 加装 4TB SATA 盘,RAID 卡 BIOS 里设成了 JBOD 模式,但系统识别成/dev/sdb,而lsblk显示它已有两个隐藏分区(厂商预置的诊断分区)。这时候parted会报错“无法识别分区类型”,GParted 直接灰掉“删除”按钮——但fdisk /dev/sdb进去一看,p打印出来清清楚楚:/dev/sdb1类型0x12(Compaq Diagnostics),/dev/sdb2类型0xee(GPT 容器)。两行d命令就能干掉,w保存后parted立刻恢复正常。这种“看得见、删得掉、改得准”的确定性,就是 fdisk 不可替代的底气。它不解决所有问题,但它永远是你排查分区类问题的第一站——就像医生不会跳过听诊器直接上 CT。
2. fdisk 的设计哲学与底层逻辑:为什么它长这样?
2.1 交互式命令行的本质:不是“软件”,而是“协议终端”
很多人把fdisk当成一个普通命令,其实它更接近一个“磁盘分区协议的交互式终端”。它的设计完全遵循 PC BIOS 时代的 MBR(Master Boot Record)规范,而这个规范至今仍是 x86 架构下最广泛兼容的分区标准。MBR 结构极其简单:512 字节的引导扇区,前 446 字节是引导代码,接着是 64 字节的分区表(4 个 16 字节条目),最后 2 字节是魔数0x55AA。fdisk的所有操作,本质上都是在读写这 64 字节的内存映像,并在写入时校验魔数、计算 CHS 地址(柱面-磁头-扇区)、验证起始扇区对齐——这些细节,GUI 工具早已帮你屏蔽,但fdisk把它们全部摊开给你看。
举个典型例子:当你输入n创建新分区时,fdisk会问:
Partition type p primary (default) e extended Select (default p):这里的选择不是“功能差异”,而是物理结构约束。MBR 规范只允许最多 4 个主分区(primary),或 3 个主分区 + 1 个扩展分区(extended),而扩展分区内部可以再划出多个逻辑分区(logical)。fdisk强制你做这个选择,是因为它必须严格按 MBR 格式生成分区表条目——选p就往主分区槽位写,选e就占用一个主槽位来指向扩展分区起始位置。如果你强行想建第 5 个主分区,fdisk会直接拒绝:“No free sectors available in the selected region.”——这不是软件限制,是硬件规范铁律。
再看单位问题。fdisk默认显示和输入的单位是“扇区”(sector),通常是 512 字节。但你会看到提示:“First sector (2048-83886079, default 2048):”。为什么默认从 2048 开始?因为现代硬盘(尤其是 Advanced Format 4K 扇区盘)要求分区起始对齐到 1MB 边界(2048×512=1MB),否则性能暴跌。fdisk不解释“为什么是 2048”,它只告诉你“这是合法范围”,逼你去查文档——这种“不友好”,恰恰是它尊重硬件真实性的体现。
2.2 与 parted/gdisk 的根本区别:抽象层级决定适用场景
| 特性 | fdisk | parted | gdisk |
|---|---|---|---|
| 目标分区表 | MBR(DOS) | MBR/GPT(抽象层) | GPT(原生) |
| 单位默认 | 扇区(512B) | MB/GB(用户友好) | 扇区(但支持 MiB/GiB) |
| 交互模式 | 命令驱动(d/p/n/w) | 命令+脚本混合 | 类 fdisk 但支持 GPT 专属命令 |
| 安全性机制 | 无内存缓存,w才写盘 | 内存缓存,quit时才确认写入 | 同 parted,但 GPT 备份头校验更严 |
| 典型使用场景 | 服务器初始化、故障恢复、嵌入式刷机 | 日常桌面分区、脚本自动化 | 新式 UEFI 系统、大容量磁盘(>2TB) |
关键点在于:parted是一个“分区管理抽象层”,它把 MBR 和 GPT 当作不同后端,向上提供统一接口;而fdisk是“MBR 专用协议终端”,它不抽象,只忠实实现 MBR 规范。所以当你在 CentOS 7 上用parted /dev/sdb mkpart primary ext4 0% 100%,它背后可能调用fdisk写 MBR,也可能调用sg_write_buffer写 GPT——你不知道,也不需要知道。但如果你要确保某块盘绝对用 MBR(比如为老 BIOS 主板装系统),fdisk是唯一能让你 100% 确认分区表类型和结构的工具。
提示:
fdisk -l输出中,如果看到 “Disk label type: dos”,说明是 MBR;如果是 “gpt”,那其实是fdisk在兼容模式下读取 GPT,此时它无法编辑 GPT 分区(会提示 “GPT PMBR size mismatch”),必须换gdisk。这个细节常被忽略,却是判断工具是否“真正生效”的第一关。
2.3 为什么“主分区右键没有删除字样”?——GUI 工具的权限与抽象陷阱
网络热词里反复出现“主分区右键没有删除字样”,这绝不是 bug,而是 GUI 工具(如 GParted、Disks)的主动保护策略。它们在检测到以下任一情况时,会禁用删除按钮:
- 分区已挂载(
mount | grep /dev/sdb1有输出) - 分区是根文件系统(
findmnt /返回该设备) - 分区包含 LVM PV 或 RAID 成员(
pvs/mdadm --detail /dev/sdb1有结果) - 分区类型为
0x12(Compaq Diagnostics)、0xef(EFI System)等系统保留类型
而fdisk没有这些检查——它只管分区表结构。你可以对一个正在挂载的/dev/sdb1执行d删除,w写入后,内核会立刻失效该设备节点,lsblk立刻消失,但如果你没先umount,正在读写的进程会收到I/O error并可能崩溃。GUI 工具的“不能删”,是帮你规避风险;fdisk的“能删”,是给你终极控制权——代价是你要自己承担后果。这就是专业工具的分水岭:前者是保姆,后者是手术刀。
3. fdisk 实操全流程详解:从零开始创建、编辑、删除、显示分区
3.1 准备工作:识别目标磁盘与安全前提
在执行任何fdisk操作前,必须完成三件事,缺一不可:
第一步:确认目标磁盘设备名
# 查看所有块设备及其大小(排除 loop 设备和 dm-*) lsblk -d -o NAME,SIZE,RO,TYPE,MOUNTPOINT # 更精确地过滤物理磁盘(排除 NVMe 的命名空间和 USB 设备) sudo lshw -class disk -short | grep -E "(disk|storage)" | grep -v "loop\|nvme.*n1\|usb" # 关键验证:确认该盘未被 LVM/RAID 占用 sudo pvs # 应无该盘输出 sudo vgs # 应无相关卷组 sudo mdadm --detail --scan | grep -q "/dev/sdb" && echo "WARNING: RAID member!" || echo "Safe"注意:
/dev/sdb是常见命名,但实际可能是/dev/nvme0n1(NVMe)、/dev/mmcblk0(eMMC)、/dev/xvda(Xen 虚拟机)。务必用lsblk确认,不要凭经验猜测。曾有同事在 AWS EC2 上误将/dev/xvda(系统盘)当成数据盘操作,导致实例无法启动。
第二步:备份原始分区表(强制!)
# 备份 MBR(512 字节)和分区表(前 512 字节) sudo dd if=/dev/sdb of=/tmp/sdb_mbr_backup.bin bs=512 count=1 # 备份整个磁盘前 1MB(含 MBR 和可能的 bootcode) sudo dd if=/dev/sdb of=/tmp/sdb_first_mb.bin bs=1M count=1 # 验证备份完整性(MD5 应一致) md5sum /tmp/sdb_mbr_backup.bin sudo dd if=/dev/sdb bs=512 count=1 2>/dev/null | md5sum提示:备份文件必须存到其他磁盘(如
/home/backup/),绝不能存在/dev/sdb自身或其子分区上。我见过三次因备份存错位置,操作失败后连恢复基础都做不到。
第三步:卸载所有关联分区
# 列出该盘所有分区及挂载点 lsblk /dev/sdb -o NAME,MOUNTPOINT # 逐个卸载(注意:/dev/sdb1、/dev/sdb2 等) sudo umount /dev/sdb1 sudo umount /dev/sdb2 # 如果提示 “target is busy”,用 lsof 查进程 sudo lsof +D /mnt/data # 替换为实际挂载点 sudo fuser -v /mnt/data # 更直观显示占用进程 # 杀死进程(谨慎!) sudo fuser -k /mnt/data3.2 创建新分区:主分区、扩展分区与逻辑分区的实操差异
以一块 20GB 的新 SATA 盘/dev/sdb为例,演示三种分区创建方式:
场景 1:创建单个主分区(最常用)
sudo fdisk /dev/sdb # 进入交互界面 Command (m for help): p # 先打印当前状态(应为空) Disk /dev/sdb: 20 GiB, 21474836480 bytes, 41943040 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x00000000 Command (m for help): n # 新建分区 Partition type p primary (default) e extended Select (default p): p # 选主分区 Partition number (1-4, default 1): 1 # 分区号 1 First sector (2048-41943039, default 2048): 2048 # 起始扇区(1MB 对齐) Last sector, +sectors or +size{K,M,G,T,P} (2048-41943039, default 41943039): +10G # 结束位置:+10G # fdisk 自动计算:10G = 10×1024×1024×1024 / 512 = 20971520 扇区,起始 2048 → 结束 20973567 Command (m for help): p # 再次查看 /dev/sdb1 2048 20973567 20971520 10G 83 Linux # 类型 83 = Linux native Command (m for help): w # 写入并退出 The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.实操心得:
+10G输入比手动算扇区数安全得多。fdisk内部会按 512 字节扇区换算,且自动向下取整到最近扇区,避免越界错误。但要注意:+10G是“至少 10GB”,实际分配可能略多(因扇区对齐),p命令显示的20971520扇区才是精确值。
场景 2:创建扩展分区 + 逻辑分区(突破 4 分区限制)
sudo fdisk /dev/sdb Command (m for help): d # 先删掉刚建的 sdb1(如果存在) Selected partition 1 Partition 1 has been deleted. Command (m for help): n Partition type p primary (default) e extended Select (default p): e # 选扩展分区 Partition number (1-4, default 1): 1 # 占用主分区槽位 1 First sector (2048-41943039, default 2048): 2048 Last sector, +sectors or +size{K,M,G,T,P} (2048-41943039, default 41943039): +15G # 扩展分区占 15G Command (m for help): n All primary partitions are in use. Adding logical partition 5 # 自动进入逻辑分区流程 First sector (31459328-41943039, default 31459328): # 起始自动设为扩展分区起始 Last sector, +sectors or +size{K,M,G,T,P} (31459328-41943039, default 41943039): +4G Command (m for help): p Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 31459327 31457280 15G 5 Extended /dev/sdb5 4096 31459327 31455232 15G 83 Linux # 逻辑分区编号从 5 开始 # 注意:sdb5 的 Start 是 4096,不是 2048 —— 因为扩展分区内部有额外的 EBR(Extended Boot Record)结构 Command (m for help): w关键原理:逻辑分区不是直接写在 MBR 里,而是通过链式 EBR 实现。每个逻辑分区前有一个 512 字节的 EBR,记录下一个逻辑分区位置。
fdisk自动管理这个链,你只需关心起始/结束扇区。但要知道:逻辑分区的最小大小受 EBR 开销限制,通常不能小于 1MB。
场景 3:创建 EFI 系统分区(UEFI 启动必需)
sudo fdisk /dev/sdb Command (m for help): n Partition type p primary (default) e extended Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-41943039, default 2048): 2048 Last sector, +sectors or +size{K,M,G,T,P} (2048-41943039, default 41943039): +512M Command (m for help): t # 修改分区类型 Selected partition 1 Hex code (type L to list all codes): L # 列出所有类型 ... ef EFI (FAT-12/16/32) ... Hex code (type L to list all codes): ef # 设为 EFI 类型 Command (m for help): p /dev/sdb1 2048 1050623 1048576 512M ef EFI System # 类型 ef Command (m for help): w注意事项:EFI 分区必须格式化为 FAT32(
mkfs.fat -F32 /dev/sdb1),且挂载到/boot/efi。类型代码ef是 UEFI 固件识别的关键,设错则无法启动。
3.3 编辑现有分区:调整大小与修改类型
调整分区大小(仅支持扩展分区内的逻辑分区)fdisk不能直接调整主分区大小(如sdb1),因为 MBR 分区表只存起始/结束扇区,不存文件系统元数据。要扩容主分区,必须:
- 用
fdisk删除原分区(d) - 用
n重建分区,起始扇区必须与原分区完全相同(否则数据丢失) - 结束扇区设为更大值
w写入后,用resize2fs /dev/sdb1(ext4)或xfs_growfs /mount/point(XFS)扩容文件系统
但对逻辑分区(如sdb5),fdisk支持直接调整:
sudo fdisk /dev/sdb Command (m for help): p Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 31459327 31457280 15G 5 Extended /dev/sdb5 4096 31459327 31455232 15G 83 Linux Command (m for help): d Partition number (1,5, default 5): 5 # 只删逻辑分区,扩展分区 sdb1 保留 Command (m for help): n All primary partitions are in use. Adding logical partition 5 First sector (4096-31459327, default 4096): 4096 Last sector, +sectors or +size{K,M,G,T,P} (4096-31459327, default 31459327): +18G # 扩大到 18G Command (m for help): p # 查看新尺寸 /dev/sdb5 4096 37752831 37748736 18G 83 Linux Command (m for help): w # 之后运行:sudo resize2fs /dev/sdb5修改分区类型(关键系统标识)
sudo fdisk /dev/sdb Command (m for help): p Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 1050623 1048576 512M 83 Linux Command (m for help): t # 修改类型 Selected partition 1 Hex code (type L to list all codes): L ... 82 Linux swap / Solaris 83 Linux 8e Linux LVM ... Hex code (type L to list all codes): 82 # 改为 swap Command (m for help): p /dev/sdb1 2048 1050623 1048576 512M 82 Linux swap / Solaris Command (m for help): w # 之后运行:sudo mkswap /dev/sdb1 && sudo swapon /dev/sdb1重要:分区类型代码(Id)只是给操作系统看的标签,不影响数据。但 swap、LVM、EFI 等类型,内核和 initramfs 会据此自动启用对应功能。设错可能导致系统无法识别 swap 或启动失败。
3.4 删除分区:安全与彻底的两种方式
方式 1:交互式删除(推荐用于单个分区)
sudo fdisk /dev/sdb Command (m for help): p Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 1050623 1048576 512M 82 Linux swap / Solaris /dev/sdb2 1050624 31459327 30408704 14G 83 Linux Command (m for help): d Partition number (1,2, default 2): 1 # 删除 sdb1 Partition 1 has been deleted. Command (m for help): p # 确认已删 Device Boot Start End Sectors Size Id Type /dev/sdb2 1050624 31459327 30408704 14G 83 Linux Command (m for help): w # 写入方式 2:清空整个磁盘分区表(灾难恢复必备)
sudo fdisk /dev/sdb Command (m for help): o # 创建新的空 DOS 分区表(覆盖 MBR) Created a new DOS disklabel with disk identifier 0x12345678. Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks. # 此时 lsblk 显示 /dev/sdb 下无任何分区对比:
o命令比连续按d删除所有分区更快、更彻底。它直接重写 MBR 的 64 字节分区表为全 0,而d只是把各条目设为 0。o后lsblk立刻清空,d+w后可能残留旧设备节点需partprobe刷新。
3.5 显示分区信息:fdisk -l 的深度解读
fdisk -l是诊断核心命令,但输出需逐行解读:
sudo fdisk -l /dev/sdb Disk /dev/sdb: 20 GiB, 21474836480 bytes, 41943040 sectors Units: sectors of 1 * 512 = 512 bytes # 单位:扇区 × 512 字节 Sector size (logical/physical): 512 bytes / 512 bytes # 硬件扇区大小 I/O size (minimum/optimal): 512 bytes / 512 bytes # 最小/最佳 IO 大小 Disklabel type: dos # 分区表类型:dos=MBR Disk identifier: 0x1a2b3c4d # MBR 磁盘标识符(随机生成) Device Boot Start End Sectors Size Id Type /dev/sdb1 2048 1050623 1048576 512M 82 Linux swap / Solaris /dev/sdb2 1050624 31459327 30408704 14G 83 Linux- Boot 列:
*表示活动分区(MBR 启动标志),仅对主分区有效。逻辑分区永不显示*。 - Start/End/Sectors:起始/结束扇区号,
Sectors是数量。计算大小:(End - Start + 1) × 512字节。 - Id 列:十六进制类型码,
82=swap,83=Linux,8e=LVM,fd=Linux RAID auto。 - Type 列:
fdisk根据 Id 码翻译的名称,但不保证准确(如83可能是 ext4/xfs/btrfs,fdisk不识别文件系统)。
实战技巧:当
fdisk -l显示Disklabel type: unknown,说明分区表损坏或非标准格式(如某些加密盘)。此时testdisk工具比fdisk更擅长修复。
4. 高频问题与避坑指南:那些官方文档不会告诉你的细节
4.1 “fdisk -l 不显示新插入的 USB 盘”——设备扫描与缓存问题
现象:插上 USB 盘,lsblk看不到/dev/sdc,fdisk -l也无输出。
排查步骤:
# 1. 检查内核是否识别到设备 dmesg | tail -20 | grep -i "usb\|sd" # 应看到类似:[ 1234.567890] sd 2:0:0:0: [sdc] 3907029168 512-byte logical blocks # 2. 如果 dmesg 有识别,但 /dev/sdc 不存在,可能是 udev 规则延迟 sudo udevadm trigger --subsystem-match=block sudo udevadm settle # 等待 udev 完成 # 3. 如果 dmesg 无识别,检查 USB 供电或线缆(尤其 USB 3.0 设备在 USB 2.0 插座上可能不识别)经验:在嵌入式 Linux(如树莓派)上,USB 存储设备有时需要
echo '1' > /sys/bus/usb/drivers/usb-storage/bind手动绑定驱动。fdisk本身不负责设备发现,它只操作已存在的/dev/xxx节点。
4.2 “删除分区后 lsblk 仍显示旧分区”——内核分区缓存未刷新
现象:fdisk /dev/sdb→d→w后,lsblk仍显示/dev/sdb1。
根本原因:Linux 内核维护分区缓存,fdisk的w命令调用BLKRRPARTioctl 通知内核重读,但某些情况下(如设备忙、RAID 层干扰)会失败。
解决方案:
# 方案 1:强制内核重读分区表(最常用) sudo partprobe /dev/sdb # 方案 2:如果 partprobe 无效,尝试 kpartx(针对 multipath 设备) sudo kpartx -d /dev/sdb # 删除映射 sudo kpartx -a /dev/sdb # 重新映射 # 方案 3:终极手段——重启内核模块(慎用) sudo modprobe -r sd_mod sudo modprobe sd_mod注意:
partprobe不会卸载已挂载分区,它只刷新内核的分区表视图。如果分区仍在使用,partprobe可能失败并提示 “device is busy”。
4.3 “fdisk 创建的分区无法格式化:Invalid argument”——对齐错误
现象:sudo mkfs.ext4 /dev/sdb1报错 “Invalid argument”。
原因分析:分区起始扇区未对齐到存储设备的最佳 IO 边界。现代 SSD/NVMe 要求 4KB 对齐(8 个 512B 扇区),而fdisk默认从 2048(1MB)开始是安全的,但如果手动输入1作为起始扇区就会出错。
验证方法:
# 查看设备物理扇区大小和对齐偏移 sudo cat /sys/block/sdb/queue/logical_block_size # 应为 512 sudo cat /sys/block/sdb/queue/physical_block_size # 常为 4096 sudo cat /sys/block/sdb/alignment_offset # 应为 0(表示对齐) # 计算分区起始是否对齐:(Start × 512) % PhysicalBlockSize == 0 # 如 Start=2048 → 2048×512=1048576 → 1048576 % 4096 = 0 ✓ # 如 Start=1 → 1×512=512 → 512 % 4096 = 512 ≠ 0 ✗修复:用fdisk重新创建分区,起始扇区务必用默认值(2048)或确保(Start × 512) % 4096 == 0。
4.4 “主分区删除后,扩展分区自动消失”——MBR 结构连锁反应
现象:fdisk /dev/sdb中,先删sdb1(主分区),再删sdb2(扩展分区),p显示只剩sdb5(逻辑分区),但sdb2已不见。
原理:MBR 中,扩展分区条目(Id=5)指向一个 EBR 扇区,该 EBR 包含逻辑分区链表。当sdb2(扩展分区)被删除,MBR 中该条目清零,内核不再识别扩展分区结构,sdb5变成“孤儿分区”,lsblk可能不显示它。
恢复方法:
# 1. 用 testdisk 扫描恢复(最可靠) sudo testdisk /dev/sdb # 选择 “Proceed” → “Intel” → “Analyse” → “Quick Search” → 找到 sdb2 和 sdb5 → “Write” # 2. 手动重建(需知道原 sdb2 起始扇区) sudo fdisk /dev/sdb Command (m for help): n Partition type: e First sector: [原 sdb2 Start] Last sector: [原 sdb2 End] Command (m for help): w # 然后用 fdisk 进入,用 `n` 重建 sdb5教训:删除扩展分区前,务必先记下其 Start/End 扇区(
p命令输出),或用sfdisk -d /dev/sdb > backup.txt备份完整分区表。
4.5 “fdisk 在 LVM 物理卷上操作失败”——LVM 元数据冲突
现象:对 LVM PV(如/dev/sdb)执行fdisk,提示 “Device contains 'LVM2_member' signature” 并拒绝操作。
原因:LVM 在设备开头写入了元数据签名(LVM2 001),fdisk检测到后出于安全考虑阻止写入。
正确做法:
# 方案 1:先移除 LVM 签名(如果确定不再用 LVM) sudo pvremove /dev/sdb # 方案 2:用 LVM 命令管理(推荐) sudo pvdisplay /dev/sdb # 查看 PV 信息 sudo vgremove <vg_name> # 删除卷组 sudo pvremove /dev/sdb # 清除 PV 签名 # 方案 3:强制 fdisk(不推荐,可能破坏 LVM) sudo fdisk -c /dev/sdb # -c 关闭 DOS