Last Updated: 2023-06-13 09:44:50 Tuesday
-- TOC --
MBR和GPT是两种不同的硬盘分区方式,它们与系统启动以及OS的支持都有关系。
Windows系统中每个盘符对应一个分区。Linux系统中没有盘符的概念,所有的分区都挂载(mount)在根目录(/)中的某个分支上,就一棵树!
MBR是Master Boot Record的缩写,它位于启动硬盘的第一个扇区(sector),512 Bytes。对于使用传统BIOS启动的计算机,BIOS在完成POST(Power On Self Test)后,根据CMOS中设定的启动顺序选择引导设备(硬盘,光盘,网络,U盘等等)。如果引导设备设置为硬盘,那么BIOS就会去读取硬盘的第一个扇区的这512个字节。
BIOS将MBR的512字节读取到内存的0x7C00地址,然后让CPU跳转到这里开始执行!
这512个字节里面,包含了一段引导程序,一个分区表和最后两个字节的Magic Number。其结构如下图:
512字节的MBR包含3部分:
分区表的长度只有64字节,里面又分成4项,每项16字节。所以,一个MBR硬盘最多只能分出4个一级分区,又叫做主分区
。每个主分区的16个字节,由如下6个部分组成:
只能有1个激活分区
。最后的4个字节(主分区的扇区总数),决定了这个主分区的容量。也就是说,一个主分区的扇区总数最多不超过2的32次方。每个扇区为512个字节,这就意味着单个主分区最大不超过2TB
。再考虑到扇区的逻辑地址也是32位的,所以单个硬盘可利用的空间最大也不超过2TB
。
随着硬盘越来越大,4个主分区也许不够,需要更多的分区。但是,分区表只有4项,因此MBR技术规定,有且仅有1个主分区,可以被定义成扩展分区(Extended partition)
。所谓扩展分区,就是指这个区里面又分成多个区。这种分区里面的分区,就叫做逻辑分区
(logical partition)。程序先读取扩展分区的第一个扇区,叫做"扩展引导记录"(Extended boot record,缩写为EBR
)。它里面也包含一张64字节的分区表,但是最多只有两项(也就是两个逻辑分区)。计算机程序接着读取第二个逻辑分区的第一个扇区,再从里面的分区表中找到第三个逻辑分区的位置,以此类推,直到某个逻辑分区的分区表只包含它自身为止(即只有一个分区项)。因此,扩展分区可以包含无数个逻辑分区。
在虚拟机中安装Ubuntu桌面,它默认就使用了一个扩展分区,在此扩展分区中,包含一个逻辑分区,此逻辑分区映射为根。
2T成了MBR硬盘的容量上限,有限制就会有突破,这就是GPT硬盘。(由于硬盘制造商采用1:1000进行单位换算,因此也有2.2TB的说法)
如果只是用mkfs对硬盘的某个分区进行格式化(删除所有文件,重新创建文件系统),用fdisk -l
看到的分区类型(文件系统类型)很可能是失真的(fdisk读取的是MBR中的分区类型字段)。此时:
df -T
命令查看分区类型sudo file -s /dev/sdc1
查看分区类型lsblk -f
查看GPT是GUID Partition Table的缩写,其含义为“全局唯一标识磁盘分区表”,GUID就是全局唯一标识符(Globally Unique Identifier)的简写。GUID分区表(GPT)是作为Universal Extensible Firmware Interface(UEFI)计划的一部分引入的,一个比较独立的技术部分。(有些软件使用UUID这个词,与GUID是一会事儿)
参考资料:理解BIOS和UEFI
GPT用GUID来表示分区类型,也必然要突破硬盘容量2T的限制!GPT还提供两个备份区域,提升系统整体健壮性。备份区域在最后,下文的测试发现,部分备份区域紧挨着最后一块分区的末尾,并没有在最后。
硬盘的第1个扇区还是MBR,不过成了protective MBR,有的地方称PMBR
。
在PMBR扇区中,没有引导程序,分区表内只有一个表项,这个表项描述了一个类型为0xEE
的分区(0xEE就是GPT类型),分区起始地址是1号扇区,大小为四个字节所能存储的最大值。该分区的存在可以使MBR-only的计算机程序认为这个磁盘是合法的,并且已被使用,从而不再去试图对其进行分区、格式化等操作,起到保护的作用。
EFI根本不使用这个分区表。
使用fdisk对硬盘进行GPT分区时,要先用
g
命令创建GPT分区表,然后再一个个创建具体分区。
网上有些文章把GPT分区说成EFI分区,EFI部分(GPT头前8个字节的签名,EFI PART
).....都是指一个东西。
其实,从上图可以看出来,整块硬盘(被使用的部分)被分成了4个部分:GPT头、分区表、GPT分区、备份区域。
GPT头
GPT头位于GPT磁盘的第2个扇区,即1号扇区(LBA1)
,该扇区是在创建GPT磁盘时生成的,GPT头定义分区表的起始位置、分区表的结束位置、每个分区表项的大小、分区表项的个数,以及分区表的checksum等信息。GPT头包含头和分区表的checksum,这样就可以及时发现错误。
LBA用8字节记录
,突破了MBR用4字节记录造成的硬盘2T容量限制。
都是CRC32。
GPT分区表
GPT没有规定最大分区数量,但一般分区工具最大支持分128个区。每个分区表项大小为128字节,这样每512bytes的sector可以存放4个分区信息。每个分区表项中记录着分区的起始和结束地址、分区类型的GUID、分区名字、分区属性。
注意,扇区尺寸不能假定为512字节,也就是说,一个扇区内可能存放4个以上的分区项。也就是说,除了硬盘的前两个扇区(LBA0和LBA1)之外,GPT规范仅定义了数据结构的尺寸,而不关心使用多少个扇区进行存储。
好多GUID,硬盘分区表中有一个GUID用于表示硬盘,每个分区用预先固定下来的GUID来表达类型,再用一个GUID来作为标识。
网上找到一份GPT分区类型的说明,供参考:
相关操作系统 | GUID[little endian] | 含义 |
---|---|---|
None | 00000000-0000-0000-0000-000000000000 | 未使用 |
None | 024DEE41-33E7-11D3-9D69-0008C781F39F | MBR分区表 |
None | C12A7328-F81F-11D2-BA4B-00A0C93EC93B | EFI系统分区[EFI System partition (ESP)],必须是FAT32格式 |
None | BC13C2FF-59E6-4262-A352-B275FD6F7172 | 扩展boot分区,必须是VFAT格式 |
None | 21686148-6449-6E6F-744E-656564454649 | BIOS引导分区,其对应的ASCII字符串是"Hah!IdontNeedEFI"。 |
None | D3BFE2DE-3DAF-11DF-BA40-E3A556D89593 | Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology) |
Windows | E3C9E316-0B5C-4DB8-817D-F92DF00215AE | 微软保留分区(MSR) |
Windows | EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | 基本数据分区 |
Windows | DE94BBA4-06D1-4D40-A16A-BFD50179D6AC | Windows恢复环境 |
Linux | 0FC63DAF-8483-4772-8E79-3D69D8477DE4 | 数据分区,这个GUID是由 GPT fdisk 和 GNU Parted 开发者根据Linux传统的 8300 分区代码创造。 |
Linux | 44479540-F297-41B2-9AF7-D131D5F0458A | x86根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载 |
Linux | 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709 | x86-64根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载 |
Linux | 69DAD710-2CE4-4E3C-B16C-21A1D49ABED3 | ARM32根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载 |
Linux | B921B045-1DF0-41C3-AF44-4C6F280D3FAE | AArch64根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载 |
Linux | 3B8F8425-20E0-4F3B-907F-1A25A76F98E8 | 服务器数据分区(/srv) 这是systemd的发明,可用于无fstab时的自动挂载 |
Linux | 933AC7E1-2EB4-4F13-B844-0E14E2AEF915 | HOME分区 (/home) 这是systemd的发明,可用于无fstab时的自动挂载 |
Linux | 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F | 交换分区(swap) 不是systemd的发明,但同样可用于无fstab时的自动挂载 |
Linux | A19D880F-05FC-4D3B-A006-743F0F84911E | RAID分区 |
Linux | E6D6D379-F507-44C2-A23C-238F2A3DF928 | 逻辑卷管理器(LVM)分区 |
Linux | 8DA63339-0007-60C0-C436-083AC8230908 | 保留 |
硬盘分区就像给一间空荡的房子划分出卧室,厨房,客厅等相互隔离的空间一样。主要是为了方便用户的使用。一方面,通过合理的硬盘分区,有效保护系统盘空间,确实能够提高系统运行速度,再者,硬盘分区也可以有效地对数据进行保护。你当然可以不分区,只不过,当你面对越来越多的子目录,或者是越来越慢的Windows,不得不费功夫去管理你的文件,或者重装Windows的时候,恐怕会悔不当初。 “不要把所有的鸡蛋放在同一个篮子里”,这句至理名言在经济学以外的其他领域也同样是句警世恒言。
一般情况下,系统分区和数据分区相互独立,在重装系统的时候会比较安全和方便。
$ sudo fdisk -l /dev/xxx
$ df -h
使用 df -h
命令,可以非常方便的查看每个分区的mount point。
fdisk工具的使用体验很不错,交互式的,只有最后输入w
,才会将所有修改写入硬盘。刚进入命令交互式环境,建议用p
查看一下命令行键入的硬盘是否正确,如果不正确,后果可能很严重。fdisk默认创建MBR分区,如果要创建GPT分区,要先使用g
命令。不管用fdisk做那种分区,默认都会建议用户不要使用硬盘的前1MB区域,即分区起始扇区LBA为2048。
分区不是格式化,格式化是创建文件系统,要使用mkfs命令。
使用fdisk必须sudo,而且这个命令有一定危险性。如果仅仅是查看系统中的block device,建议使用lsblk命令。lsblk命令也能够将分区信息显示出来,查看文件系统类型,添加-f
参数。
xxd命令用来查看二进制的输入,可以用xxd命令直接读取设备文件并显示。
用一块U盘做测试。
$ sudo fdisk /dev/sdb
Welcome to fdisk (util-linux 2.38).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/sdb: 28.65 GiB, 30765219840 bytes, 60088320 sectors
Disk model: SanDisk 3.2Gen1
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: 0xff4d60d9
Device Boot Start End Sectors Size Id Type
/dev/sdb1 2048 60088319 60086272 28.7G 83 Linux
Command (m for help): g
Created a new GPT disklabel (GUID: 4F7A3610-F722-1248-B05C-5506A7048CBB).
The device contains 'dos' signature and it will be removed by a write command. See fdisk(8) man page and --wipe option for more details.
Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-60088286, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-60088286, default 60086271):
Created a new partition 1 of type 'Linux filesystem' and of size 28.7 GiB.
Partition #1 contains a vfat signature.
Do you want to remove the signature? [Y]es/[N]o: y
The signature will be removed by a write command.
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
$ lsblk /dev/sdb
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb 8:16 1 28.7G 0 disk
└─sdb1 8:17 1 28.7G 0 part
$ sudo fdisk /dev/sdb -l
[sudo] password for xinlin:
Disk /dev/sdb: 28.65 GiB, 30765219840 bytes, 60088320 sectors
Disk model: SanDisk 3.2Gen1
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
Disk identifier: 4F7A3610-F722-1248-B05C-5506A7048CBB
Device Start End Sectors Size Type
/dev/sdb1 2048 60086271 60084224 28.7G Linux filesystem
用xxd命令查看LBA0:
$ sudo xxd -a -l512 /dev/sdb
[sudo] password for xinlin:
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000001c0: 0200 eeff ffff 0100 0000 ffdf 9403 0000 ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa ..............U.
PMBR中存在一个分区表项,该表项地址从446开始,大小为16字节:
$ sudo xxd -s446 -l16 /dev/sdb
000001be: 0000 0200 eeff ffff 0100 0000 ffdf 9403 ................
非激活,类型0xEE,按little endian解读第1个扇区的逻辑地址和扇区总数:
$ python -c 'print(0x00000001)'
1
$ python -c 'print(0x0394dfff)'
60088319
前面用fdisk看到整块U盘的扇区总数是60088320,上面的数据很靠谱。
下面查看GPT头(LBA1)信息:
$ sudo xxd -a -s512 -l512 -u /dev/sdb
00000200: 4546 4920 5041 5254 0000 0100 5C00 0000 EFI PART....\...
00000210: C148 E123 0000 0000 0100 0000 0000 0000 .H.#............
00000220: FFDF 9403 0000 0000 0008 0000 0000 0000 ................
00000230: DEDF 9403 0000 0000 1036 7A4F 22F7 4812 .........6zO".H.
00000240: B05C 5506 A704 8CBB 0200 0000 0000 0000 .\U.............
00000250: 8000 0000 8000 0000 6C7E CC5C 0000 0000 ........l~.\....
00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000003f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
一眼就能看到,最开始的8字节EFI PART签名。中间的16字节Disk identifier: 4F7A3610-F722-1248-B05C-5506A7048CBB,存储在LBA1偏移0x38开始的位值:
$ sudo xxd -s$((512+0x38)) -l16 -u /dev/sdb
00000238: 1036 7A4F 22F7 4812 B05C 5506 A704 8CBB .6zO".H..\U.....
版本号,1.0,值为0000 0100
GPT头大小,92字节,5C00 0000
然后是CRC校验和(计算时置0),C148 E123
接着是保留的4字节,全0
下面是GPT头所在扇区,8字节,0100 0000 0000 0000
,这就是LBA1
然后是GPT头的备份扇区,8字节,FFDF 9403 0000 0000
,值为:
$ python -c 'print(0x000000000394dfff)'
60088319
这刚好是U盘的最后一个扇区,其内容如下:
$ sudo xxd -a -s$((512*60088319)) -l512 -u /dev/sdb
729bffe00: 4546 4920 5041 5254 0000 0100 5C00 0000 EFI PART....\...
729bffe10: 61E2 F253 0000 0000 FFDF 9403 0000 0000 a..S............
729bffe20: 0100 0000 0000 0000 0008 0000 0000 0000 ................
729bffe30: DEDF 9403 0000 0000 1036 7A4F 22F7 4812 .........6zO".H.
729bffe40: B05C 5506 A704 8CBB DFDF 9403 0000 0000 .\U.............
729bffe50: 8000 0000 8000 0000 6C7E CC5C 0000 0000 ........l~.\....
729bffe60: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
729bffff0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
仔细观察就能发现,这块备份GPT头的内容,所在LBA,和备份LBA,与LBA1中的内容刚好交换了一下,这就是相互备份呀。
再然后,就是GPT分区起始扇区,0008 0000 0000 0000
,和终止扇区,DEDF 9403 0000 0000
,分别是:
$ python -c 'print(0x0000000000000800)'
2048
$ python -c 'print(0x000000000394dfde)'
60088286
前面用fdisk分区时,使用的是默认提供的值60086271,我们来做一组简单的小学数学题:
>>> 60088286 - 2048 + 1
60086239 # 整个GPT分区的大小
>>> 60088286 - 60086271 + 1
2016 # GPT分区剩余未分配区域
>>> 60088319 - 60088286 + 1
34 # GPT分区末端到U盘末端的扇区数
>>> (60086271 - 2048 + 1) / 8
7510528.0 # GPT分区大小是4K倍数,8*512=4096
总有一些扇区隐藏在分区之外...
GPT分区表开始的位值,0200 0000 0000 0000
,LBA2。
分区表的总项数,以及每个分区表所占用字节数,8000 0000 8000 0000
,都是128。
前面的步骤说明,我只分了一个区,下面是LBA2的前128字节的内容:
$ sudo xxd -a -s$((512*2)) -l128 -u /dev/sdb
[sudo] password for xinlin:
00000400: AF3D C60F 8384 7247 8E79 3D69 D847 7DE4 .=....rG.y=i.G}.
00000410: E23D 8E8F AAF7 4906 8DAD B4C4 E804 6A54 .=....I.......jT
00000420: 0008 0000 0000 0000 FFD7 9403 0000 0000 ................
00000430: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
00000470: 0000 0000 0000 0000 0000 0000 0000 0000 ................
前面32字节,是2个GUID
起始扇区,0008 0000 0000 0000
,即2048
结束扇区,FFD7 9403 0000 0000
,即60086271
属性和人类可读区域全0。
所有信息和数据都能够对上!
最后,备份扇区记录了一个GPT分区表的备份扇区,这个扇区的位置,在GPT分区后面第1个扇区:
$ sudo xxd -a -s$((60088287*512)) -l128 -u /dev/sdb
[sudo] password for xinlin:
729bfbe00: AF3D C60F 8384 7247 8E79 3D69 D847 7DE4 .=....rG.y=i.G}.
729bfbe10: E23D 8E8F AAF7 4906 8DAD B4C4 E804 6A54 .=....I.......jT
729bfbe20: 0008 0000 0000 0000 FFD7 9403 0000 0000 ................
729bfbe30: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
729bfbe70: 0000 0000 0000 0000 0000 0000 0000 0000 ................
与LBA2前128字节完全一样!
UEFI规定,ESP分区的GUID:C12A7328-F81F-11D2-BA4B-00A0C93EC93B
,FAT32文件系统。UEFI系统在ESP分区中寻找bootloader。
下面的数据,来自我的移动fedora系统:
Disk /dev/sdb: 335.35 GiB, 360080695296 bytes, 703282608 sectors
Disk model: SL500 360GB
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 33553920 bytes
Disklabel type: gpt
Disk identifier: B11F9587-9643-4155-B0B4-9815BABC9029
Device Start End Sectors Size Type
/dev/sdb1 65535 1245164 1179630 576M EFI System
/dev/sdb2 1245165 3407819 2162655 1G Linux filesystem
/dev/sdb3 3407820 703190549 699782730 333.7G Linux filesystem
首先查看GTP分区表所在扇区:
$ sudo xxd -a -s512 -l512 /dev/sdb
00000200: 4546 4920 5041 5254 0000 0100 5c00 0000 EFI PART....\...
00000210: cf8f bd35 0000 0000 0100 0000 0000 0000 ...5............
00000220: af3d eb29 0000 0000 2200 0000 0000 0000 .=.)....".......
00000230: 8e3d eb29 0000 0000 8795 1fb1 4396 5541 .=.)........C.UA
00000240: b0b4 9815 babc 9029 0200 0000 0000 0000 .......)........
00000250: 8000 0000 8000 0000 787a 9ce6 0000 0000 ........xz......
00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000003f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
GPT分区表开始的位值,0200 0000 0000 0000
,LBA2。我们查看第1项的128字节信息:
$ sudo xxd -a -u -s1024 -l128 /dev/sdb
00000400: 2873 2AC1 1FF8 D211 BA4B 00A0 C93E C93B (s*......K...>.;
00000410: BCCE 2CB2 9D73 E045 8C6A 2151 78F3 F00F ..,..s.E.j!Qx...
00000420: FFFF 0000 0000 0000 ECFF 1200 0000 0000 ................
00000430: 0000 0000 0000 0000 4500 4600 4900 2000 ........E.F.I. .
00000440: 5300 7900 7300 7400 6500 6D00 2000 5000 S.y.s.t.e.m. .P.
00000450: 6100 7200 7400 6900 7400 6900 6F00 6E00 a.r.t.i.t.i.o.n.
00000460: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000470: 0000 0000 0000 0000 0000 0000 0000 0000 ................
前16字节,就是ESP的GUID,起始扇区为LBA 65535(FFFF),右侧清晰可见ASCII字符串,EFI...
这就是/dev/sdb1
分区,FAT32,它被挂载在/boot/efi
路径:
$ df -T
Filesystem Type 1K-blocks Used Available Use% Mounted on
devtmpfs devtmpfs 4096 0 4096 0% /dev
tmpfs tmpfs 3964808 0 3964808 0% /dev/shm
tmpfs tmpfs 1585924 1940 1583984 1% /run
/dev/sdb3 btrfs 349891364 63776908 285565204 19% /
tmpfs tmpfs 3964812 16 3964796 1% /tmp
/dev/sdb3 btrfs 349891364 63776908 285565204 19% /home
/dev/sdb2 ext4 1028904 301300 657156 32% /boot
/dev/sdb1 vfat 588636 17780 570856 4% /boot/efi
tmpfs tmpfs 792960 220 792740 1% /run/user/1000
这个分区下,包含/efi/boot路径:
$ sudo ls -l /boot/efi/efi
total 8
drwx------. 2 root root 4096 Sep 12 2022 BOOT
drwx------. 2 root root 4096 Apr 29 11:46 fedora
我在介绍将Grub装入U盘的笔记中,实践了这种情况:U盘甚至都可以不分区(一整块U盘就是一个分区),只需要FAT32文件系统,有/efi/boot/bootx64.efi文件即可。这个efi程序就是grub。这可能是UEFI的fallback机制,或者是非标实现。
本文链接:https://cs.pynote.net/hd/hdisk/202110165/
-- EOF --
-- MORE --