☰
gdisk实战:GPT分区规划、MBR转GPT与扩容修复
2026/10/27 16:31:32 网站建设 项目流程

前阵子帮人做一批存储节点的初始化,其中有台机器的数据盘从 2TB 换成了 8TB。同事沿用了几年前的装机脚本,脚本里是一串fdisk的交互按键,跑完之后lsblk看着分区齐整,挂载也没报错,可df -h一出来,整块盘只认到 2TB 出头。原因不复杂:脚本里那串按键在旧盘上走的是 MBR 分区表,而 MBR 用 32 位记录扇区地址,单个分区、乃至整块盘的可寻址范围天然卡在 2TiB 附近。盘换了,分区方式没换,多出来的容量就成了一块谁也摸不到的死区。

这类事情碰得多了以后,我处理磁盘的第一步基本上固定下来了:先看清楚盘上现在是什么分区表,再决定用哪把刀。gdisk就是在这个环节里我最常用的工具,它专门操作 GPT 分区表,交互式界面把"改内存"和"写磁盘"两件事分得很清楚,用熟了以后误操作的代价会比一路回车小得多。下面这些内容,是我这几年用 gdisk 做分区规划、MBR 转 GPT、扩容修复时攒下来的经验,从底层结构讲到具体按键,再到几种高频报错的排查路径,适合刚接触 Linux 磁盘管理的人跟着走一遍,也适合已经会用fdisk但被 GPT 相关报错卡住过的人对照着看。

1. gdisk 维护的不是一张分区表,而是三份互相校验的副本

1.1 一块 GPT 盘从头到尾到底存了哪些东西

很多人对分区表的想象是一张纸,写一次就完事。GPT 不是这样,它更像一份带校验码、还做了异地备份的档案,同一份信息在磁盘上至少存在三处。搞清楚这三处的位置,后面遇到的各种"备份头不在末尾""主备不一致"的报错,就都有了解释。

从 LBA 0 开始数(LBA 是逻辑块地址,一个块通常是 512 字节,4K 原生盘上就是 4096 字节):LBA 0 是保护性 MBR,里面只有一个类型为0xEE的分区项,作用是让不认识 GPT 的老工具看到这块盘时,误以为它被一个占满全盘的分区占着,从而不敢乱动;LBA 1 是主 GPT 头,里面存着磁盘 GUID、主分区条目数组的起始 LBA、备份头的 LBA、第一个和最后一个可用 LBA、分区条目数量与单项大小,以及两个 CRC32 校验值;LBA 2 到 LBA 33 是分区条目数组,默认 128 项、每项 128 字节,正好 16KiB,占 32 个 512 字节扇区;磁盘末尾再原样存一份备份头加备份分区条目数组。

两个 CRC32 是关键设计。一个校验头部自身(前 92 字节),一个校验整个分区条目数组。gdisk 每次启动都会把这两处算一遍,只要有一处对不上,它就会在开屏那几行里告诉你,并且倾向于用"看起来更可信"的那一份去重建另一份。所以你会看到类似Caution: invalid backup GPT header, but valid main header; regenerating backup header from main header.这样的提示——这不是错误,是它在做自愈。

1.2 gdisk 开屏那几行提示,其实是在给磁盘做体检

直接敲gdisk /dev/sdb,屏幕上先出来的是版本号、磁盘型号、容量、扇区大小,然后是分区表扫描结果。这几行信息量很大,值得养成逐行读一遍的习惯,而不是急着按?。

正常情况下它会显示GPT fdisk (gdisk) version x.x.x,接着是Partition table scan:下面几行MBR: protective、BSD: not present、APM: not present、GPT: present,最后是Found valid GPT with protective MBR; using GPT.。这说明三份结构互相吻合,可以放心操作。如果某一行显示corrupt或者MBR: MBR only,那就是另一套处理流程了,后面第 3 节会展开。

还有一类容易被忽略的提示是磁盘尺寸变化。虚拟机扩容、RAID 阵列重组、iSCSI 目标端调整容量之后,GPT 头部记录的"最后一个可用 LBA"还是老值,磁盘末尾的备份头也就停在了老位置。gdisk 会明确告诉你备份头不在磁盘末尾,顺手还会报一句Warning! Disk size mismatch!。这时候别急着建分区,先把备份头搬到新位置,否则你新建的分区可能正好压在备份头上。

1.3 新版 fdisk 也能建 GPT,那 gdisk 的价值在哪

先说清楚一件事:util-linux 从 2.23 版本开始,fdisk就已经支持 GPT 了,parted更是早就支持。所以"fdisk 不能建 GPT"这个说法在今天并不成立。但我在实际工作里依然优先选 gdisk,理由不是功能缺失,而是行为可预期性。

对比项gdisk新版 fdiskparted
GPT 专用程度专为 GPT 设计,所有菜单围绕 GPT 语义GPT 是后加的支持,与 MBR 模式共用一套界面通用,MBR/GPT 都做
写盘时机所有改动只在内存,按w才落盘,q直接丢弃同样需要w,但部分版本会提示更少命令即生效,没有"确认写盘"这一步
备份头位置异常明确报错,并提供x菜单的e一键搬迁提示较弱,部分版本直接重建行为不透明,容易在不知情下重建
分区名(GPT name 字段)支持c修改,最长 36 字符支持有限支持
混合 MBR有专门菜单,风险提示充分不支持不支持
备份/还原分区表支持二进制备份与回灌不支持不支持
脚本化弱,主要靠 sgdisk支持-开头的部分参数支持-s脚本模式

最实用的一条是"改动只在内存"。我见过不止一次,有人在parted里敲了rm 3回车,想反悔的时候已经晚了。gdisk 里删错分区,只要还没按w,直接q退出,磁盘上一个字节都没变。这个特性在给生产机器做操作时价值极高,尤其是远程 SSH 上去、网络还不太稳的场景。

2. 交互界面里的每个字母,背后都是一次独立的磁盘动作

2.1 主菜单里真正天天用的只有七个键

按?能列出全部指令,但真正常用的就那么几个。下面这张表是我自己贴在便签上的版本,按使用频率排序。

按键作用使用频次备注
p打印当前分区表极高每次操作前后都该看一眼
n新建分区极高会依次问编号、起始、结束、类型码
w写入磁盘并退出极高唯一真正落盘的动作
q不保存退出极高反悔专用,养成肌肉记忆
d删除分区高只删分区项,不动数据
t修改分区类型码高见 2.3
i查看单个分区详情高能看到 GUID、属性位、分区名
v校验分区表中写盘前跑一次,能提前发现问题
c修改分区名中注意不是文件系统标签
x进入专家菜单中对齐值、搬备份头、改磁盘 GUID
r进入恢复/转换菜单中MBR 转换、备份回灌、混合 MBR
o新建空 GPT低相当于清空重来,慎用
s分区排序低一般用不上
b备份分区表到文件低但关键大改动前必做

这里要特别强调d的行为。它删除的是分区条目,也就是"这块扇区范围归属哪个分区"的记录,扇区里的数据一个都没动。所以在误删之后、还没写入之前,q退出就完全没事;即使已经写入了,只要没在上面新建分区、没做文件系统操作,用相同起始扇区重建一个同类型分区,数据通常还能捞回来。这个"通常"是有前提的,详见第 5 节的排查链路。

2.2 从一块空盘走到 EFI + 根分区 + swap 的完整流程

假设/dev/sdb是一块全新的 1TB 盘,目标布局是:512MiB 的 EFI 系统分区、64GiB 的根分区、16GiB 的 swap,剩下的空间留给数据分区。纯手工敲一遍是这个样子。

gdisk /dev/sdb

进入交互界面后,如果盘上有旧的分区表残留,可以先o建一个新的空 GPT(注意这会清掉所有分区条目),然后:

Command (? for help): n Partition number (1-128, default 1): 1 First sector (34-1953525134, default = 2048) or {+-}size{KMGTP}: Last sector (2048-1953525134, default = 1953525134) or {+-}size{KMGTP}: +512M Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): ef00 Changed partition type from 'Linux filesystem' to 'EFI System'

起始扇区直接回车,gdisk 会用默认值 2048,这个数字不是随便定的,它代表 1MiB 偏移,是绝大多数 SSD 和机械盘的最佳对齐点,2.4 节会细说。结束扇区写+512M,表示从起始位置往后 512MiB,gdisk 自己换算成扇区号,比手工算512*1024*1024/512靠谱得多。

接着建根分区:

Command (? for help): n Partition number (2-128, default 2): First sector (1050624-1953525134, default = 1050624) or {+-}size{KMGTP}: Last sector (1050624-1953525134, default = 1953525134) or {+-}size{KMGTP}: +64G Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300):

这里类型码直接回车就是8300,即 Linux filesystem。swap 分区同理,只是类型码填8200。最后一个数据分区,结束扇区直接回车,gdisk 会自动取到磁盘末尾的最后一个可用扇区,不会压到备份 GPT 头上——这一点比手工算扇区号安全太多。

全部建完,先p看一眼布局,再v跑一次校验,最后w落盘。写入时 gdisk 会做一次"最终检查",把可能有问题的点提示出来,比如分区重叠、备份头位置不对、有分区没对齐。看到Final checks complete.之后再按Y,落盘就完成了。如果落盘后终端出现The kernel is still using the old partition table.,说明内核还没重新读分区表,跑一次partprobe /dev/sdb即可,这一点第 5 节会详细说。

2.3 类型码选错,比分区大小算错更难查

分区类型码是 GPT 里一个 16 字节的 GUID,gdisk 允许你输入四位十六进制简写。它不影响分区能不能挂载,但会影响一堆上层工具的判断逻辑,选错了往往表现为"系统装不上""阵列组不起来""开机找不到引导",排查起来比容量问题绕得多。

场景类型码GUID 简写含义选错的典型后果
UEFI 引导分区ef00EFI System安装器不识别引导分区,报"未找到 EFI 分区"
普通 Linux 数据/根分区8300Linux filesystem一般无影响,但部分安装器的自动分区逻辑会跳过
Linux swap8200Linux swapswapon仍可用,但自动挂载脚本和休眠恢复可能失效
LVM 物理卷8e00Linux LVMpvcreate能过,但某些发行版安装器不会自动把它纳入
软 RAID 成员fd00Linux RAIDmdadm --assemble的自动扫描可能扫不到,需手工指定
LUKS 加密卷8309Linux LUKS桌面环境的自动解锁弹窗可能不触发
Windows 基本数据0700Microsoft basic dataWindows 磁盘管理里显示为未知分区
Windows 保留分区0c01Microsoft reserved系统盘上缺这个分区,某些版本拒绝安装

ef00是最需要盯紧的一个。有些主板固件在枚举启动项时,只看类型码为 EFI System 的分区里的EFI/BOOT/BOOTX64.EFI,类型码填成8300就算文件放对了位置也照样找不到。我在一台机器上折腾过两个小时,最后发现就是当初建分区时手快,类型码留了默认值。

软 RAID 的fd00也是重灾区。mdadm组装阵列时有一段自动扫描逻辑,它会按分区类型码和超级块双重要求去匹配。类型码写错的情况下,mdadm --assemble --scan静悄悄地什么都不做,你还以为阵列坏了。用mdadm --examine /dev/sdb1能看到超级块明明在,就是扫不到,这时候回头看一眼gdisk里的类型码,往往就水落石出了。

2.4 对齐值 gdisk 已经替你算好了,但你得知道它算的是什么

gdisk 默认把新分区的起始扇区对齐到 2048 扇区边界。在 512 字节逻辑扇区的盘上,2048 × 512 = 1MiB;在 4K 原生盘上,2048 × 4096 = 8MiB。两种情况下都是 1MiB 的整数倍,而 1MiB 能被绝大多数 SSD 的擦除块大小(512KiB 或 1MiB)、RAID 条带宽度(常见 64KiB 到 1MiB)、以及文件系统的块组大小整除。

不做对齐的代价在机械盘上不明显,在 SSD 上会实打实地体现在写入放大和寿命上。一个典型的错位场景是:分区起始扇区是 63(这是老式 MBR 时代留下的习惯),在 4K 物理扇区、512 字节逻辑扇区的盘上,每一次 4K 写入都会跨两个物理扇区,SSD 主控不得不做读-改-写,性能直接腰斩。

想看当前的对齐值,进x菜单按d:

Command (? for help): x Expert command (? for help): d Using 2048 sectors as the default alignment value

想改,在同一个菜单里按l,输入新的扇区数。什么时候需要改?一种是老式硬件 RAID 卡,条带宽度不是 2 的整数次幂,对齐值可能要调成条带大小的整数倍;另一种是某些嵌入式设备,扇区大小非标准。除此之外,2048 这个默认值几乎没有改的必要。

还有一点容易忽略:n时手工指定起始扇区,如果你敲了个1000这种非对齐值,gdisk 不会拦你,它只在v校验时提醒一句。所以在手工指定扇区之后,务必跑一次校验。

3. 把 MBR 盘转成 GPT:先想清楚退路再动手

3.1 动手之前必须确认的三件事

MBR 转 GPT 是不可逆的(严格说有办法转回去,但代价很大),而且有几种情况会导致转换后系统直接起不来。所以在敲任何命令之前,这三件事必须先确认清楚。

第一,盘上有没有逻辑分区。MBR 的主分区表只有 4 个条目,超过 4 个分区就要靠扩展分区加逻辑分区链。gdisk 的 MBR 转 GPT 只转换主分区,逻辑分区在转换过程中会被直接丢弃。这不是 bug,是 GPT 没有扩展分区这个概念,没法一一对应。如果你的盘上有/dev/sdb5这种编号的分区,先想办法把数据迁走,别指望原地转换。

第二,分区有没有跨过 2TiB 这条线。MBR 的分区项用 32 位记录起始 LBA 和扇区数,512 字节扇区下上限是 2TiB。如果原盘已经有分区越过这条线(有些工具会用一些非常规手段硬撑),转换过程中这些分区的边界信息会失真。

第三,也是最重要的一条:这块盘是怎么引导的。如果机器是用传统 BIOS 从这块盘启动的,转成 GPT 之后主板固件根本读不懂,直接表现为开机黑屏或者"no bootable device"。这种情况下要么改 UEFI 引导,要么做混合 MBR(见 3.3),没有第三条路。

确认方法很简单:

lsblk -o NAME,SIZE,TYPE,FSTYPE,PARTTYPENAME sudo fdisk -l /dev/sdb sudo parted /dev/sdb print

parted输出里的Partition Table:那一行会直接告诉你msdos还是gpt。

3.2 gdisk 打开 MBR 盘时那句提示的含义

用 gdisk 打开一块纯 MBR 盘,会看到这样一行:

Found invalid GPT and valid MBR; converting MBR to GPT format in memory.

这句话经常被误读成"已经转换完成了"。实际上它只做了一件事:把 MBR 里的分区项读出来,在内存里按 GPT 的格式组织好,等你确认。磁盘上什么都没有变,你随时可以q退出。

这时候按p,你会看到转换后的 GPT 布局,主分区都在,类型码按 MBR 的类型做了映射(0x83映射到8300,0x82映射到8200,0x8e映射到8e00,0xfd映射到fd00)。逻辑分区如果存在,这里就不会出现——这也是判断有没有丢分区的直接方法,先记下原来fdisk -l里的分区数量,对比一下。

确认无误后按w,gdisk 会先问一次Do you want to proceed?,然后写入。写入的过程包括:重写 LBA 0 的保护性 MBR、写主 GPT 头、写分区条目数组、在磁盘末尾写备份。写完之后内核需要重读分区表,否则/dev/sdb1这些设备节点还是老的。

有一条经验值得记下来:转换完成后,分区的 PARTUUID 全部变了。原来/etc/fstab里如果写的是PARTUUID=xxxx,重启之后必然挂载失败。用 UUID(文件系统自己的 UUID)写的则不受影响,因为文件系统本身没动。这也是我在 fstab 里更倾向用 UUID 的原因之一。

3.3 混合 MBR 是一个正在退场的妥协方案

混合 MBR 的思路是:LBA 0 仍然放一个 MBR,里面既有保护性的0xEE项,又额外放最多三个指向真实分区的主分区项。这样 BIOS 固件能看到可引导分区,GPT 结构也还在,两套体系各取所需。

在 gdisk 里,进r菜单按h就能配置。它会列出当前分区,让你输入要把哪几个分区"暴露"给 MBR,以及每个分区的 MBR 类型码和可引导标志。一个典型配置是把 EFI 分区或/boot所在分区设成0x07或0x83并标记为活动。

我不推荐这个东西,原因有三。一是它把一份分区信息存了两份,两份之间一旦不同步就会出现极为诡异的引导问题;二是暴露的分区最多三个(第四个槽位被0xEE占了),分区一多就得取舍;三是现代主板基本都支持 UEFI 了,连十年前的机器都少见纯 BIOS 环境。只有在确实要维护老旧设备、又必须用上 GPT 的大容量盘时,才值得考虑。

真要用,配完之后记得用fdisk -l验证一下,确认 MBR 那一侧看到的分区和你预期一致。gdisk 的r菜单里按o可以打印保护性 MBR 的内容。

3.4 备份与回灌:b键和r菜单里的那几项

做任何大改动之前,备份分区表是一件成本极低、收益极高的事。gdisk 的b键会把整个 GPT 结构(保护性 MBR、主头、分区条目数组)导出成一个二进制文件,默认路径是/root/sdb-gpt.bak之类的名字,你自己可以改。

Command (? for help): b Filename to save the GPT data to: /root/sdb-gpt-20240115.bak

这个文件大约 17KiB,用file看会显示成x86 boot sector之类的东西,别被吓到,它就是原始扇区数据。想更土一点的做法是直接dd:

sudo dd if=/dev/sdb of=/root/sdb-first-34sectors.bin bs=512 count=34

恢复的时候,gdisk 的r菜单里有从备份文件加载的选项,选中备份文件后,它会重写主 GPT 头和分区条目数组。备份尾部的备份头需要靠x菜单的e命令重新写到磁盘末尾。

这里有个坑要提醒。备份文件里记录了磁盘 GUID 和磁盘总容量,跨盘回灌是要出问题的。我曾经在批量部署时想省事,把一台机器的分区表备份灌到另一台同型号机器上,结果因为磁盘 GUID 冲突,后来的systemd在生成/dev/disk/by-partuuid链接时出现了难以解释的行为。正确做法是:备份文件只用于同一块盘的恢复,批量部署用sgdisk逐盘重建。

4. 一百台机器要分区,交互模式就是在浪费生命

4.1 gdisk 自带的命令行参数只够"看一眼"

gdisk 本身支持少量非交互参数,最常用的是-l:

sudo gdisk -l /dev/sdb

这个命令不开交互界面,直接把分区表打印出来,输出格式和交互模式下的p一致。它适合放在监控脚本里,或者在做任何操作前先存一份现场快照。

还有-b可以指定备份文件的路径,-B在某些版本上用来从备份文件加载。但这些能力就到头了,gdisk的设计哲学就是交互式工具,批量操作不是它的主场。真要做批量,得上同套件里的sgdisk。

sudo gdisk -l /dev/sdb > /var/log/sdb-partition-snapshot-$(date +%F).txt

这条命令我现在基本会塞进所有涉及磁盘操作的脚本开头,出问题时回头看,比凭记忆强太多。

4.2 sgdisk 一条命令完成分区、命名加类型

sgdisk是同一个项目里的脚本化版本,所有操作通过参数完成,不进入任何菜单。下面是一条我常用的"初始化一块数据盘"命令:

sudo sgdisk -Z /dev/sdb sudo sgdisk \ -n 1:0:+512M -t 1:ef00 -c 1:"EFI System" \ -n 2:0:+64G -t 2:8300 -c 2:"root" \ -n 3:0:+16G -t 3:8200 -c 3:"swap" \ -n 4:0:0 -t 4:8300 -c 4:"data" \ /dev/sdb

-Z是彻底擦除,会把 GPT 和 MBR 的结构全部清掉,相当于把盘恢复成"出厂状态"。如果盘上有残留的 RAID 超级块或者文件系统签名,-Z一并清掉。这个参数威力很大,敲之前务必确认设备名。-o则温和一些,只创建一个空 GPT,不动其他区域。

-n的三个字段是分区号:起始扇区:结束扇区。起始写0表示由sgdisk自动选择第一个可用扇区,并且会做对齐处理;结束写+64G表示从起始往后 64GiB;结束写0表示一直用到磁盘末尾最后一个可用扇区。

-t设类型码,-c设分区名。分区名这个字段在 GPT 里可以存 36 个 UTF-16 字符,写点有意义的内容对后续维护很有帮助——lsblk加-o NAME,PARTLABEL就能直接看到。

写成脚本以后,配合设备序列号取设备名会更稳:

#!/usr/bin/env bash set -euo pipefail DISK="/dev/disk/by-id/ata-ST4000NM000A-2HZ100_ZA1B2C3D" REAL=$(readlink -f "$DISK") echo "[*] 目标设备: $REAL" lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT "$REAL" read -r -p "确认清空并重建分区表? 输入 YES 继续: " ans [ "$ans" = "YES" ] || { echo "已取消"; exit 1; } sgdisk -Z "$REAL" sgdisk -n 1:0:+512M -t 1:ef00 -c 1:"EFI" \ -n 2:0:0 -t 2:8300 -c 2:"data" \ "$REAL" partprobe "$REAL" udevadm settle sgdisk -v "$REAL"

4.3 脚本化场景下最容易翻车的三个点

第一个是设备名不固定。/dev/sdb、/dev/sdc的顺序取决于内核枚举顺序,加一块盘、改一次 BIOS 启动顺序都可能变。脚本里用/dev/disk/by-id/下的稳定路径,或者干脆用 LVM 的逻辑卷,能省掉大量半夜被叫起来处理故障的时间。

第二个是-Z的破坏性。它不光清 GPT,还会把文件系统的超级块签名一起抹掉。有一次我写脚本时贪方便,把-Z放在了判断逻辑前面,结果一个参数传错,直接清掉了一块还没备份的盘。现在我的脚本里,任何带-Z或者mkfs的命令前面都强制加一次read -p确认,这个习惯救过我好几次。

第三个是内核没有及时刷新。sgdisk写完分区表后,内核的块设备层不一定立刻感知到新分区。脚本里紧跟partprobe和udevadm settle,能大幅降低"明明建好了分区,mkfs却报找不到设备"的概率。settle那一步是等 udev 把设备节点和符号链接都创建完,少了它,紧接着执行的mkfs /dev/sdb1有可能跑在节点出现之前。

5. 磁盘扩容之后第一件事不是建分区,是搬备份头

5.1 备份 GPT 头停在老位置的后果

虚拟机磁盘从 100G 扩到 500G,这是运维里最常见的操作之一。在宿主机侧调整完容量,进到 guest 里敲lsblk,能看到盘变大了,但gdisk一打开就报错:

Warning! Disk size mismatch! This is an error condition

或者更直白的:

Caution: The backup GPT header is not at the end of the disk.

原因在 1.1 节说过:GPT 在磁盘末尾存了一份备份头和备份分区条目数组,这个"末尾"的位置是写死在磁盘上的。磁盘变大以后,实际的末尾往后移了,备份头还停在原来的位置。这时候如果你在靠近末尾的地方建分区,新分区就会正好压在备份头上,GPT 的两份副本彻底打架。

修复只需要三步:

Command (? for help): x Expert command (? for help): e Expert command (? for help): w

e是 "relocate backup data structures to the end of the disk",gdisk 会重新计算新的末尾位置,把备份头和备份分区条目数组搬过去。w落盘。命令行版本更简单:

sudo sgdisk -e /dev/sdb

这个过程不动主 GPT 头和分区条目数组,理论上没有数据风险,但保险起见还是先把-b备份做掉。另外,搬完之后记得partprobe,让内核重新读一遍。

还有一个顺序问题:先搬备份头,再扩分区。如果你先growpart或者先删了重建,分区可能会覆盖掉旧的备份头位置,虽然 GPT 的备份本来就是可再生的,但凭空多一层麻烦。

5.2 主头和备头打架时,先判断哪一份可信

比"备份头位置不对"更麻烦的是"主备内容不一致"。这种情况通常出现在异常断电、虚拟机强制关机、或者 dd 写到一半被中断之后。gdisk 的反应是挑一份它认为可信的,然后提示你重建另一份。问题是它挑的未必是你想要的。

判断顺序我一般这样走。先看v的输出,它会明确列出发现的问题数量和类型。如果提示是The main GPT header is invalid but the backup is valid,那说明主头挂了,从备份恢复就行。反过来The backup GPT header is invalid but the main is valid,则用主头重建备份。

如果两份都"有效"但内容不同,事情就微妙了。这时候要看的是分区条目数组的 CRC 是否匹配对应的头部,以及两边的分区数量是否一致。gdisk 的r菜单提供了几个强制选项:b用备份头重建主头,c用备份分区表重建主分区表,d用主头重建备份头,e用主分区表重建备份分区表。选哪个取决于你判断哪一份更新、更完整。

我自己踩过的一次:一台虚拟机的盘做了快照回滚,但磁盘末尾的备份是在回滚点之后写入的,结果主头是旧的、备头是新的,两边的分区数量对不上。最后是分别用p打印两份(r菜单里可以单独打印备份分区表),肉眼比对分区边界,判断主头那份才是快照时刻的真实状态,用d和e把备份重建了。这种活没有捷径,只能靠比对。

症状可能原因处理动作
备份头不在末尾磁盘扩容x→e→w
主头 CRC 错,备头好断电、写入中断r→b(备份重建主头)
备头 CRC 错,主头好断电、备份区被覆盖r→d(主头重建备份)
两边都有效但分区数不同快照回滚、多路径读写打印两份比对,选更新的一份重建另一份
全部损坏分区表被整体覆盖从b备份文件回灌,或r→f从 MBR 重建

5.3 分区表写进去了,内核却装作没看见

w之后看到The operation has completed successfully.,但lsblk里还是老样子,这是极高频的问题。最直接的原因是内核没能重新读取分区表,而内核重读失败最常见的原因是分区正在被使用。

Linux 的BLKRRPARTioctl 要求块设备上没有活跃的分区引用。只要有任何一个分区被挂载、被 swap 占用、被 LVM 激活、被 mdadm 组装、被 loop 设备或 device-mapper 引用,重读就会被拒绝,gdisk 会给出那句经典的提示:

The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8)

这时候别急着reboot。先把占用链拆干净:

sudo umount /dev/sdb1 sudo swapoff /dev/sdb2 sudo vgchange -an <卷组名> sudo mdadm --stop /dev/md0 sudo dmsetup ls sudo dmsetup remove <映射名>

拆完再partprobe /dev/sdb,或者blockdev --rereadpt /dev/sdb。还不行的话,fuser -mv /dev/sdb1和lsof | grep sdb能帮你找到是谁还抓着不放。实在没办法,只能重启,但重启前务必确认分区表已经正确落盘——gdisk -l看到的是磁盘上的真实内容,它对了就没问题。

我自己遇到过一次特别隐蔽的:分区没挂载,LVM 也没激活,但一个早已退出的dd进程留下了僵尸的子进程,句柄还挂在/dev/sdb上。fuser一看就出来了,kill -9之后就正常了。所以排查占用链的时候,fuser和lsof这两个工具不能省。

6. 分区名、文件系统标签、UUID:三个世界的名字别混用

6.1 gdisk 的c改的到底是什么

这是我在给新人做培训时必讲的一个点。gdisk 里按c改的是GPT 分区名,它是分区条目里的一个字段,存在分区表里,最大 36 个字符。它跟文件系统标签、跟设备节点的 soft link、跟fstab里写的东西,全都没有关系。

想看到它,用lsblk -o NAME,PARTLABEL,或者gdisk里按i看Partition name:那一行。想改文件系统标签,得用文件系统自己的工具:

sudo e2label /dev/sdb2 "data-vol" # ext2/3/4 sudo xfs_admin -L "data-vol" /dev/sdb2 # xfs sudo fatlabel /dev/sdb1 "EFI" # vfat sudo ntfslabel /dev/sdb3 "WinData" # ntfs sudo btrfs filesystem label /mnt/point "data-vol"

这两套名字的用途完全不同。GPT 分区名是给分区表看的,UEFI 固件在枚举启动项时会读它,systemd会用它生成/dev/disk/by-partlabel/下的符号链接,一些集群管理工具(比如某些版本的 Ceph 部署脚本)会用它来识别磁盘角色。文件系统标签是给文件系统看的,blkid能读出来,fstab里可以写 `LABEL

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

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

立即咨询