搞了几次花活导致系统引导进不去了,修复好后决定研究下过程,先从 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 对齐,简化用户操作并避免兼容性问题。
发表回复