gdisk命令使用这件事,我踩过的坑大概比很多人用过的次数还多。最早接手一批单盘容量超过 2TiB 的存储节点时,fdisk建完分区一写盘容量就对不上,折腾半天才反应过来——MBR 分区表的天花板就在那里,2TiB 卡死,跟磁盘本身、文件系统都没关系。从那时候起,gdisk就成了我装机流程里的固定成员。它专门处理 GPT 分区表,能在不重启、不丢数据的前提下完成新建、删除、改类型、改名字、备份恢复乃至 MBR/GPT 互转这一整套动作。这篇内容我会把 gdisk 命令使用过程中真正需要理解的东西摊开讲:它和 fdisk、parted 的定位差别,GPT 那几个容易混淆的概念,一块空盘从零到挂载的完整会话,出问题之后的排查路径,以及怎么用 sgdisk 把它脚本化。适合刚接触 Linux 磁盘管理的新手,也适合已经会用但没搞明白原理、踩坑只能靠重装的人。
1. 先搞清楚 gdisk 的定位,别拿它当 fdisk 用
1.1 MBR 天花板与 GPT 的由来
很多人第一次接触gdisk是因为一个很具体的报错:在容量大于 2TiB 的磁盘上,fdisk建出来的分区最大只能到 2TiB,剩下的空间像凭空蒸发了一样。这属于 MBR 分区表本身的结构限制,跟工具无关。MBR 用 4 字节(32 位)记录分区的起始 LBA 和长度,按 512 字节扇区算,32 位能表示的最大扇区号是 2³²,也就是 2TiB。磁盘再大,分区表本身也表达不了。
GPT 换了一套思路。它用 64 位记录 LBA,理论上限是 2⁶⁴ 个扇区,按 512B 扇区计算约 8ZiB,实际使用中早已远超任何单块磁盘的容量。分区项数量也从 MBR 的 4 个主分区扩展到默认 128 个,且没有主分区、扩展分区、逻辑分区这套历史包袱。另外 GPT 在磁盘头部写一份分区表,在磁盘尾部再写一份完整的备份,头部损坏时可以用尾部数据重建——这一点在真实故障里救命过不止一次。
所以选择逻辑很清晰:单盘容量超过 2TiB,或者一台机器上要建超过 4 个分区又不想折腾扩展分区,就该用 GPT。这里有个细节,现代fdisk(util-linux 2.23 以后)其实也支持 GPT 了,输入g就能切换。但它的 GPT 功能相对基础,缺少备份恢复、混合 MBR、分区表校验这些进阶能力。我个人的习惯是:临时看一眼用fdisk -l,真正要动分区表,尤其是生产环境,用gdisk。
1.2 gdisk、fdisk、parted、sgdisk 各自的活儿
这四个工具经常被混着提,但分工其实挺明确。fdisk是老牌交互式工具,支持 MBR 和(较新版本)GPT,界面简单,适合快速查看和小改动。parted的强项是支持调整已有分区大小,以及一条命令搞定建表建分区,但它的交互式界面在某些版本上对 GPT 的细节控制不够细。gdisk是 GPT 专家工具,交互式界面里能改分区名、分区类型码、GUID、属性位,还有专门的恢复子菜单。sgdisk则是同一套底层的命令行版本,专为脚本设计。
我一般这么用:日常查看lsblk加gdisk -l;要建 GPT 分区表且需要精确控制,进gdisk交互界面;批量部署或者写自动化脚本,用sgdisk;需要在线缩小分区,parted或者resize2fs配合。这几个工具不是互斥的,切换着用完全没问题,因为它们操作的是同一份磁盘上的数据结构。
有一点必须强调:所有分区工具操作的都是分区表,不碰分区里的数据。但分区表一改,分区边界就变了,分区里原有的文件系统可能立刻变得不可访问。所以在任何写操作之前,先确认目标盘上没有正在使用的重要数据,或者已经做好了块级备份。这条不是危言耸听,我在早期项目里就因为把w和q看反过一次,一条命令送走了整块盘的分区表。
2. GPT 的几个关键概念,不理解就等着踩坑
2.1 主备双头加上保护性 MBR 的三层结构
GPT 在磁盘上的布局分三块。第一块是 LBA 0 上的保护性 MBR(protective MBR),它只写一个类型为 0xEE 的分区项,占满整块盘。作用是让那些只认 MBR 的老工具看到这块盘时知道「这盘被占了,别乱动」,避免误判成空盘直接覆盖。这也是为什么你在gdisk里输入p,有时会看到一行提示说检测到 MBR——那是保护性 MBR,属于正常现象。
第二块是 LBA 1 上的 GPT 头,记录磁盘 GUID、分区表本身的位置和大小、CRC32 校验值、备份头所在位置等元信息。第三块紧跟在头后面是分区项数组,默认 128 项,每项 128 字节,合计 16KiB,占 32 个扇区。磁盘尾部再镜像一份头加分区项数组,作为备份。
这个结构带来的直接好处是容错。如果主头被覆盖或损坏,gdisk启动时会提示主 GPT 有问题,并询问是否用备份重建。反过来,如果磁盘尾部的备份丢了(比如磁盘做过镜像但尾部没拷全),也能用主头重建备份。我处理过一次磁盘克隆后分区表错乱的故障,就是靠gdisk自动检测加r菜单里的重建选项救回来的,全程没动分区数据。
2.2 类型码、GUID、分区名,三个完全不同的东西
新手最容易混淆的就是这三个概念,我把它们拆开说。
分区类型码(type code)是一个 16 位的十六进制值,告诉操作系统「这个分区打算用来干什么」。常用的有8300(Linux 文件系统)、8200(Linux swap)、8E00(Linux LVM)、FD00(Linux RAID)、EF00(EFI 系统分区)、0700(Microsoft 基本数据)。类型码不影响分区里实际存了什么,你完全可以把一个8300的分区格式化成 NTFS,只是某些工具会因此误判。真正需要严格对应的场景是 UEFI 引导:ESP 必须是EF00,否则固件可能不认。
分区 GUID 是每个分区唯一的 128 位标识符,由系统生成,正常情况下一辈子不用管。它的用处是当分区顺序变化、设备名从/dev/sda1变成/dev/sdb1时,还能稳定地指到同一个分区。挂载用UUID=其实就是文件系统 UUID,跟分区 GUID 是两回事,别搞混。
分区名(partition name)是一段最多 36 个字符的 UTF-8 文本,纯粹是给人看的标签,内核和大多数工具都不依赖它。但它在多盘多分区的环境里极其有用,我习惯给每个分区起名比如root、data01、backup,lsblk -o NAME,SIZE,PARTLABEL一眼就能对上号。设置分区名用主菜单的c命令。
2.3 对齐值 2048 不是随便定的
gdisk新建分区时,起始扇区默认给 2048。这个数字不是拍脑袋来的。传统机械硬盘的物理扇区是 512 字节,但内部实际读写单位(物理扇区)早就变成 4096 字节了,这就是所谓的 4K 原生盘。如果分区起始位置没和 4096 字节边界对齐,一次逻辑写操作可能横跨两个物理扇区,硬盘内部就得做读-改-写,性能损失在随机写场景下能到 20% 以上。
2048 个 512 字节扇区正好是 1MiB,是 4096 的整数倍,绝大多数场景都能对齐。这也是为什么gdisk从 LBA 34(分区项数组结束位置)之后跳到 2048 才开始放第一个分区,中间那一段是故意留白的。有些老教程教人手动把起始扇区设成 34 来「节省空间」,这是典型的捡芝麻丢西瓜,省下 1MiB 换来长期性能损失。
如果你确实需要改对齐值,进x专家菜单,用g命令调整。但除非你在做嵌入式或者有特殊布局要求,默认值别动。验证对齐可以用gdisk的v校验命令,或者事后用parted /dev/sda align-check optimal 1检查。
3. 完整实操:一块空盘从零到挂载
3.1 动手之前先确认三件事
动手前我固定会做三步检查。第一步,确认盘符没认错:lsblk -o NAME,SIZE,TYPE,MODEL,SERIAL,MOUNTPOINT看一眼,容量、型号、序列号全对上再往下走。多盘机器上/dev/sdb和/dev/sdc顺序变了导致格式化错盘,是我见过最高频的事故,而且往往是不可逆的。
第二步,确认盘没被挂载、没被 LVM 或 RAID 占用:lsblk里 MOUNTPOINT 列应为空,cat /proc/mounts | grep sdb也确认一下。如果盘上跑着 LVM,先vgchange -an把卷组停掉;跑着 swap 的,swapoff。否则gdisk写入时会提示内核还在用这块盘,需要重启才生效。
第三步,如果盘上有旧数据但你不确定要不要留,先做一次分区表备份:gdisk里输入b,指定一个文件名,它会把当前分区表存成文件。几 KB 的文件,可能省掉你几个小时的恢复工作。这三步加起来不到一分钟,但没有一步是可以省的。
3.2 交互式建分区的完整会话
假设目标是/dev/sdb,一块 4TiB 的空盘,要划两个分区:一个 512MiB 的 EFI 系统分区,剩下全部给数据分区。下面是实际敲的过程和每一步的意图。
启动工具:gdisk /dev/sdb。如果盘上是空白的或者只有 MBR,它会提示类似「Found invalid GPT and valid MBR」或者「No GPT, creating new one」,问你要不要转换。空盘的情况直接确认即可,它会自动建一份新的 GPT。
进界面后先p看一眼,确认盘容量、扇区总数、当前分区表状态符合预期。这一步别省,尤其是从别人手里接过来的机器。
新建第一个分区:输入n。提示Partition number (1-128, default 1),直接回车用默认的 1。提示First sector (34-7814037134, default = 2048),回车用默认 2048,保证 1MiB 对齐。提示Last sector, +/-sectors or +/-size{K,M,G,T,P},这里输入+512M,表示从起始位置往后 512MiB,不用自己算扇区号,gdisk会处理取整。
接着gdisk会问分区类型码,默认是8300。这里要改,输入EF00。然后问分区名,输入EFI或者直接回车跳过。分区名允许空格,但脚本化时建议别用空格,容易出转义问题。
新建第二个分区:再输入n,分区号自动变成 2。起始扇区默认会接着上一个分区的结束位置往后对齐,直接回车。结束位置提示里会显示default = 7814037134(磁盘最后一个可用扇区),直接回车就是吃满剩余空间。类型码保持默认8300,分区名给个data。
全部建完再p确认一遍。核对三件事:起始扇区是不是 2048 的整数倍(对齐),分区大小是不是符合预期,类型码对不对。确认无误后输入w写入。gdisk会弹一段警告,大意是「即将覆盖现有分区表,确认吗」,输入y。看到The operation has completed successfully.才算真正落盘。
想放弃的话用q,任何未写入的改动都会丢弃,磁盘保持原样。这是gdisk比某些工具让人安心的地方:改动都在内存里,只有w才落盘。
3.3 让内核认账,然后格式化和挂载
w之后分区表已经写到磁盘上了,但内核里的分区缓存还是旧的。多数情况下内核会自动重读,lsblk能立刻看到/dev/sdb1、/dev/sdb2。如果没看到,执行partprobe /dev/sdb或者blockdev --rereadpt /dev/sdb强制重读。如果目标盘上有分区正被使用,重读会失败并报Device or resource busy,这时候只能umount掉相关分区再试,实在不行就重启。
分区出现之后才轮到格式化。EFI 分区用mkfs.fat -F32 /dev/sdb1,注意 FAT32 在 Linux 下对小于一定容量的分区会有簇大小警告,可以忽略。数据分区看用途,mkfs.xfs -f /dev/sdb2或mkfs.ext4 -F /dev/sdb2,-f/-F是强制覆盖已有文件系统的意思,仅在确认盘上无数据时用。
格式化完blkid /dev/sdb2拿到 UUID,写进/etc/fstab。这里我强烈建议用UUID=而不是/dev/sdb2,因为设备名会随插拔顺序变化,而 UUID 是文件系统自带的、跟着数据走的。写完mount -a验证一遍,再df -h确认挂载点和容量正确。如果是开机要用的 ESP 分区,/etc/fstab里挂到/boot/efi,类型写vfat,选项加umask=0077。
3.4 一份可以直接抄的会话记录
把上面整个过程压成一份可复现的记录,用的还是/dev/sdb这块 4TiB 盘,两个分区,512MiB ESP 加剩余空间。
# 第一步:确认盘没被占用 lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,MODEL /dev/sdb # 输出里 MOUNTPOINT 一列应该全空 # 第二步:进 gdisk gdisk /dev/sdb # 下面是在 gdisk 交互界面里的完整输入序列 # p 查看当前分区表 # n 新建分区 # 1 分区号 # 2048 起始扇区(默认,回车即可) # +512M 大小 # EF00 类型码(EFI System) # EFI 分区名 # n 再建一个 # 2 分区号 # <回车> 起始扇区(默认,自动对齐) # <回车> 结束扇区(默认,吃满剩余) # 8300 类型码(Linux filesystem,默认) # data 分区名 # p 再次确认 # w 写入 # y 确认覆盖写入成功后回到 shell,接着做后半段。
# 让内核重读分区表(多数情况可省) partprobe /dev/sdb # 确认分区已经出现 lsblk -f /dev/sdb # 格式化 mkfs.fat -F32 /dev/sdb1 mkfs.xfs -f /dev/sdb2 # 取 UUID blkid /dev/sdb1 /dev/sdb2 # 挂载测试 mkdir -p /mnt/data mount /dev/sdb2 /mnt/data df -h /mnt/data这套流程我用了很多次,基本没出过问题。一个容易被忽略的点是:+512M这种写法里的单位是二进制,512MiB 不是 512MB,换算成扇区数时gdisk会自动取整并保证结束位置对齐,不需要你手动算。
4. 最容易出问题的场景和排查思路
4.1 常见报错速查
下面这张表是我这几年攒下来的,覆盖了绝大多数现场遇到的提示和对应处理。
| 现象/报错 | 触发原因 | 处理方式 |
|---|---|---|
Found invalid GPT and valid MBR | 盘上只有 MBR 或者 GPT 已损坏 | 确认数据可弃后用o重建,或进r菜单用f从 MBR 生成 GPT |
Found valid GPT with corrupt MBR | 保护性 MBR 被覆盖,GPT 本身完好 | 进x菜单用n重建保护性 MBR,不影响分区数据 |
Device or resource busy | 分区正被挂载或 swap/LVM 占用 | umount、swapoff -a、vgchange -an逐个清掉 |
写入后lsblk看不到新分区 | 内核分区缓存未刷新 | partprobe或blockdev --rereadpt,仍失败则重启 |
Warning: Partition table header claims that the size of partition table entries is ... | 磁盘被其他工具写过,元数据不一致 | 用gdisk的v校验,按提示重建备份头 |
The backup GPT header is not at the end of the disk | 磁盘做过扩容,备份头还停在旧位置 | 进x菜单用e把备份结构移到新末尾 |
| 分区起始位置不是 8 的整数倍 | 手动指定了非对齐扇区 | x菜单g改回 2048,或删了重建 |
Caution: invalid main GPT header, but valid backup | 主 GPT 头损坏 | 用备份重建主头,操作见下节 |
v(verify)这个命令值得单独说,它会把分区表的所有元数据过一遍,检查 CRC、对齐、重叠、备份一致性,有问题会列出来并给出建议。我习惯在任何写操作之前先跑一次v,尤其是接手别人维护过的机器。校验通过再动手,等于给自己买了个保险。
4.2 分区表损坏之后怎么救
损伤分几种情况,处理路径不一样。
第一种,主 GPT 头损坏但备份完好。gdisk /dev/sdb启动时会直接告诉你主头有问题、备份可用,并询问是否用备份重建。输入y让它重建,然后p检查分区是否都在,w写入。整个过程不碰分区数据。
第二种,备份头位置错了(常见于磁盘扩容、虚拟机磁盘文件被截断或镜像未完整复制)。gdisk会报警告,进x专家菜单,用e命令把备份结构重新定位到磁盘真正的末尾,然后w。这一步做完,警告消失。
第三种,主头备份头全挂,但你之前用b备份过分区表文件。这时候用gdisk的r菜单,l命令加载备份文件,检查分区项恢复情况,再回主菜单w写入。这也是我坚持在动分区表之前先b一下的原因——几 KB 的文件,成本几乎为零。
第四种,什么都没有,纯靠扫描。gdisk不带自动扫描功能,但可以用testdisk这类工具做分区表扫描重建。这条路成功率不低,但耗时长,且需要在写回之前仔细核对每个分区的起止扇区,别把数据分区边界搞错。
还有一种更粗暴的兜底:如果分区表彻底没了但你知道原来分区的起始扇区,可以在gdisk里用完全相同的起始扇区重新建一个分区,只要不动起始位置、不格式化,原来的文件系统大概率还能挂上。我救过一块 8TiB 的盘,就是靠回忆加上hdparm的输出反推出起始扇区,重建分区后xfs_repair -n检查一轮,数据全在。
4.3 磁盘扩容之后必须补的一步
虚拟化环境里给磁盘扩容是常事,但很多人扩容完发现gdisk里显示的容量还是旧的,或者直接报警告说备份头不在末尾。原因是扩容只改了后端,GPT 的备份头还停在原来磁盘的最后一个扇区位置。
正确顺序是:先在虚拟化层扩容量,然后进gdisk,它会检测到磁盘实际扇区数大于 GPT 记录的值。这时候进x专家菜单,用e命令把备份结构移到新末尾,回主菜单w写入。之后再用gdisk的d删掉最后一个分区、用相同起始扇区重建一个更大的,或者直接用growpart工具在线扩展。最后才轮到文件系统层扩容:XFS 用xfs_growfs /mountpoint,ext4 用resize2fs /dev/sdb2。
这个顺序不能乱。先扩文件系统再扩分区,等于让文件系统以为自己有空间,实际分区边界没变,写超了就是文件系统损坏。我在一次演练里见过有人反过来操作,最后靠备份恢复才收场。
5. 脚本化:sgdisk 让批量操作不用手敲
5.1 sgdisk 的参数对照关系
单机手工建分区用gdisk交互界面挺舒服,但要在几十台机器上做同样的事,就得靠sgdisk。它是gdisk包里的命令行版本,底层逻辑完全一致,只是把交互步骤变成了参数。
| 需求 | gdisk 交互操作 | sgdisk 参数 |
|---|---|---|
| 新建分区 | n | -n 1:2048:+512M |
| 设置类型码 | t | -t 1:EF00 |
| 设置分区名 | c | -c 1:"EFI" |
| 删除分区 | d | -d 1 |
| 打印分区表 | p | -p |
| 清空并新建 GPT | o | -o |
| 彻底擦除 GPT 和 MBR | 无直接对应 | -Z |
| 校验 | v | -v |
| 设置对齐扇区数 | x菜单g | -a 2048 |
| 备份分区表到文件 | b | -b backup.bin |
| 从文件恢复 | r菜单l | -l backup.bin |
| 移动备份头到磁盘末尾 | x菜单e | -e |
-n的格式是分区号:起始扇区:结束扇区。起始位置可以写具体扇区号,也可以写0表示让工具自动挑下一个对齐位置。结束位置除了具体扇区号,同样支持+512M、+100G这种相对大小写法。分区号省略的话会取第一个空闲编号。
5.2 一条命令建完整分区表
还是那块 4TiB 的/dev/sdb,两个分区的需求,用sgdisk一行写完。
sgdisk -o \ -n 1:2048:+512M -t 1:EF00 -c 1:"EFI" \ -n 2:0:0 -t 2:8300 -c 2:"data" \ /dev/sdb逐段拆开看。-o先清空所有现有分区并建一份全新 GPT,这一步相当于交互界面里的o。第一个-n 1:2048:+512M从 2048 扇区开始、占 512MiB;-t 1:EF00标成 EFI 系统分区;-c 1:"EFI"起名。第二个-n 2:0:0里的两个 0 分别是「自动选起始位置」和「用剩余空间」,sgdisk会自己算出对齐后的起点和磁盘末尾;类型码和名字照给。
执行完立刻sgdisk -p /dev/sdb看一眼结果,再partprobe让内核认账。整个过程不需要任何交互,可以原封不动塞进 shell 脚本或者配置管理工具里。
-Z和-o的区别得说清楚,因为用错代价很大。-o只清分区数据、建新 GPT,保护性 MBR 会重建;-Z是把 GPT 和 MBR 全抹掉,盘上不留任何分区表痕迹,适合彻底回收磁盘。日常重建分区表用-o就够了,除非你确定要把盘交出去或者做安全擦除。
5.3 幂等性和批量下发
脚本化最容易出的问题是不幂等——第二次跑的时候盘上已经有分区了,命令行为跟第一次不一样。sgdisk的-o本身自带清空语义,所以重复执行的结果是一致的,这一点比堆一堆if判断省事。
真正的坑在设备名。批量下发时绝对不能写死/dev/sdb,因为每台机器的盘符顺序可能不同。稳妥的做法是按磁盘序列号或者 WWN 定位。lsblk -o NAME,SERIAL,WWN能拿到这些信息,用lsblk -dno NAME /dev/disk/by-id/ata-XXXX反查出当前盘符,再传给sgdisk。多路径环境下还得注意/dev/mapper/mpathX和/dev/sdX指向同一块物理盘,别对两个设备名各建一次分区表。
还有一个细节是分区表写入后的通知。sgdisk结束后内核不一定立刻重读,脚本里应该显式加一行partprobe "$DISK"并检查返回码。如果返回非零,说明有分区在使用,脚本应该中止而不是继续往下格式化,否则可能把一个已挂载的分区给格了。这类防御性判断在自动化里比在手工操作里重要得多,因为没有人在旁边看着。
6. 几个我反复验证过的小习惯
分区名一定要起,而且要有规律。我现在所有机器的分区名都按角色+序号的格式写,比如root、data01、data02、backup。lsblk -o NAME,SIZE,PARTLABEL,MOUNTPOINT一条命令就能把整机磁盘布局看明白,比对着/etc/fstab一条条核对快得多。分区名不影响启动、不影响挂载,纯粹是给人看的,但就是这点便利,在凌晨三点排查故障的时候价值极高。
动手前先b备份分区表,这个动作我已经做成肌肉记忆了。备份文件不到 20KB,随手存到/root/gpt-backup/下面加上日期,攒一年也占不了多少空间。分区表跟分区数据是两回事,分区表丢了你还能靠备份文件或者扫描救回来,但如果连分区边界信息都没了,恢复难度会翻好几倍。
还有一点,gdisk的w是不可逆的,但它只写分区表,不动分区里的数据。所以最常见的「误操作」其实不是写错,是把该保留的分区删了之后又w了。如果删完还没写,q退出,一切照旧;如果已经写了,别慌,先别格式化、别往上写任何东西,用相同起始扇区重建分区,文件系统大概率完好无损。这个操作我实际做过好几次,XFS 和 ext4 都能救回来,前提是别在中间插一个mkfs。
最后说个 OV 的细节。UEFI 引导的机器,ESP 分区大小我是从 512MiB 起步的,因为现在内核镜像加 initramfs 越做越大,/boot里还要塞多个内核版本。见过 100MiB ESP 在系统更新几次之后撑爆导致开不了机的,那种场景修起来相当麻烦,得从 live 环境挂进去清文件。分区表建好之后再想扩 ESP,中间隔着一堆分区,操作复杂度完全不是一个量级。与其后面折腾,不如一开始就把余量留够。