GPT 分区布局

创建

搞了几次花活导致系统引导进不去了,修复好后决定研究下过程,先从 GPT 分区表开始。参考维基百科,GPT 分区表布局如下:

GPT 表以 LBA(Logical Block Address)即逻辑区块地址组织的,查看逻辑区块大小:

❯ lsblk -o NAME,FSTYPE,PHY-SEC,LOG-SEC /dev/nvme0n1
NAME        FSTYPE PHY-SEC LOG-SEC
nvme0n1                512     512
├─nvme0n1p1 vfat       512     512
└─nvme0n1p2 ext4       512     512

表示每个逻辑区块为 512 字节,shell 中执行命令:cat /dev/nvme0n1 | head -c 1000000 | hexdump -Cv >> learngpt 直接将硬盘的前一百万个字节导出到文件中,接下来学习 GPT 分区布局。第一个逻辑区块中记录了 GPT 分区表的表头,格式参考维基百科

分区表表项参考:

分析真实磁盘数据:

00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| LBA0 开始
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000190  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001c0  02 00 ee ff ff ff 01 00  00 00 af 52 77 ee 00 00  |...........Rw...|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200  45 46 49 20 50 41 52 54  00 00 01 00 5c 00 00 00  |EFI PART....\...| LBA1 起始
00000210  c8 84 00 d9 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00000220  af 52 77 ee 00 00 00 00  00 08 00 00 00 00 00 00  |.Rw.............| 第一个可用于分区的 LBA 是 LBA0x800
00000230  8e 52 77 ee 00 00 00 00  ** ** ** ** ** ** ** **  |.Rw.....********| 打 * 的部分是磁盘 GUID 我藏起来了
00000240  ** ** ** ** ** ** ** **  02 00 00 00 00 00 00 00  |********........|
00000250  80 00 00 00 80 00 00 00  f8 60 e4 00 00 00 00 00  |.........`......|
00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000270  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000290  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000300  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000310  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000320  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000330  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000350  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000360  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000370  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000380  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000400  28 73 2a c1 1f f8 d2 11  ba 4b 00 a0 c9 3e c9 3b  |(s*......K...>.;| LBA2 起始 Entry1 起始
00000410  ** ** ** ** ** ** ** **  ** ** ** ** ** ** ** **  |****************| 打 * 的部分是分区 GUID 我藏起来了
00000420  00 08 00 00 00 00 00 00  ff 07 08 00 00 00 00 00  |................|
00000430  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000440  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000450  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000460  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000470  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000480  af 3d c6 0f 83 84 72 47  8e 79 3d 69 d8 47 7d e4  |.=....rG.y=i.G}.| Entry2 起始
00000490  ** ** ** ** ** ** ** **  ** ** ** ** ** ** ** **  |****************| 打 * 的部分是分区 GUID 我藏起来了
000004a0  00 08 08 00 00 00 00 00  ff 4f 77 ee 00 00 00 00  |.........Ow.....|
000004b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00100000  eb 58 90 6d 6b 66 73 2e  66 61 74 00 02 01 20 00  |.X.mkfs.fat... .| 第一个分区开始
00100010  02 00 00 00 00 f8 00 00  20 00 40 00 00 08 00 00  |........ .@.....|
00100020  00 00 08 00 c1 0f 00 00  00 00 00 00 02 00 00 00  |................|
00100030  01 00 06 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00100040  80 01 29 73 da 05 0c 65  66 69 2d 62 6f 6f 74 20  |..)s...efi-boot |
00100050  20 20 46 41 54 33 32 20  20 20 0e 1f be 77 7c ac  |  FAT32   ...w|.|
00100060  22 c0 74 0b 56 b4 0e bb  07 00 cd 10 5e eb f0 32  |".t.V.......^..2|
00100070  e4 cd 16 cd 19 eb fe 54  68 69 73 20 69 73 20 6e  |.......This is n|
00100080  6f 74 20 61 20 62 6f 6f  74 61 62 6c 65 20 64 69  |ot a bootable di|
00100090  73 6b 2e 20 20 50 6c 65  61 73 65 20 69 6e 73 65  |sk.  Please inse|
001000a0  72 74 20 61 20 62 6f 6f  74 61 62 6c 65 20 66 6c  |rt a bootable fl|
001000b0  6f 70 70 79 20 61 6e 64  0d 0a 70 72 65 73 73 20  |oppy and..press |
001000c0  61 6e 79 20 6b 65 79 20  74 6f 20 74 72 79 20 61  |any key to try a|
001000d0  67 61 69 6e 20 2e 2e 2e  20 0d 0a 00 00 00 00 00  |gain ... .......|

需要注意的是 GUID 在小端法中的表示,比如 00000400 行表示 EFI 系统分区的 GUID 的字节序列是 28 73 2a c1 1f f8 d2 11 ba 4b 00 a0 c9 3e c9 3b,但实际上他的值应该是 C12A7328-F81F-11D2-BA4B-00A0C93EC93B,前三部分需要考虑小端法对字节序列的影响,这与 GUID 的字段含义有关,参考:GUID 的基本结构。00000480 行的字节序列是 af 3d c6 0f 83 84 72 47 8e 79 3d 69 d8 47 7d e4,转换为 GUID 即 0FC63DAF-8483-4772-8E79-3D69D8477DE4,正是 Linux 文件系统的标准 GUID。

另外根据 00000220 行的数据可以得出,第一个可用于分区的 LBA 是 LBA0x800(十进制是 2048),查看 fdisk 发现:

❯ fdisk /dev/nvme0n1
Command (m for help): p

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: gpt

Device          Start        End    Sectors  Size Type
/dev/nvme0n1p1   2048     526335     524288  256M EFI System
/dev/nvme0n1p2 526336 4000796671 4000270336  1.9T Linux filesystem

第一个分区确实是从第 2048 个区块开始的,为什么第一个分区前要空出来 2048 个分区 * 512 字节每分区 = 1MiB 的空间呢?因为哪怕 GPT 的前 34 个 LBA 全部用上也只占用了 34 LBA * 512 字节每LBA = 17 KiB 的空间,为什么直接空了 1MiB 呢?

以前,硬盘采用 CHS(柱面-磁头-扇区)寻址,
一方面,使用 INT 13h 这样的传统 BIOS API 的老系统和老引导程序要求所有分区必须按柱面对齐。
另一方面,有些系统为了提高性能,避免跨柱面访问硬盘,导致:
MBR 在第一个扇区(属于第一柱面),即 MBR 把第一个柱面占用了;所以第一个分区要从第二个柱面开始。
每个柱面 63 个扇区,即 MBR 占用第一柱面的 63 个扇区,所以第一个分区前会有 63 个扇区的空间。
MBR 大小只有一个扇区(512 字节),第一柱面剩下的空间没有利用,有些引导程序(如 grub)就拿来用了。

随着硬件的发展,许多新硬盘:
1、一个物理扇区有 4096 个字节,即 4KiB
2、使用 LBA 寻址,不再使用 CHS 寻址
如果分区的起始位置未对齐到 4KiB 的整数倍,文件系统的逻辑块可能跨越两个物理扇区。
此时,读写一个逻辑块需要操作两个物理扇区,导致性能下降。
举例:假设分区从第 63 扇区(31.5KiB)开始,其第一个 4K 簇将覆盖扇区 63~70(31.5KiB ~ 35KiB)
导致每个簇读写需操作两个物理扇区,性能损失可达 10%~20%

在保持兼容的基础上为了最大化利用磁盘性能,需要从第 63 扇区(31.5KiB)开始,挑一个能被 4KiB 整除的值就可以了。
可以是 32KiB、64KiB 或者更大的,都行。
为什么最后选择了 1MiB 这个数呢?不知道,得问微软:
自 Windows Vista 起,微软将分区对齐到 1MiB 作为默认设置,以支持高级格式磁盘。这一标准随后被广泛采纳。
主流工具如 fdisk、parted、Disk Utility 也跟进,默认采用 1MiB 对齐,简化用户操作并避免兼容性问题。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

苏ICP备2023046324号-1