硬盘的结构

先说机械硬盘(磁盘),机械硬盘由多个盘片组成,盘片表面涂有磁性材料,数据就存在这里,每个盘片有正反两面称为盘面,每个盘面都有一个对应磁头(Head)用于读写数据。


盘面被划分为有限个同心圆,这些同心圆就是磁道,所有盘面中相同半径的磁道组成一个柱面(类似圆柱体),同一个磁道会被划分为多段圆弧,这一段段圆弧称为扇区,扇区大小为512B,扇区为磁盘最小的物理存储单元。

数据寻址方式

数据寻址就是确定数据位于硬盘的哪个具体位置,好比要找到某个人在哪里就要知道他位于哪个省哪个市哪个街道等等。

寻址方式是对操作系统而言的,硬盘内部自有一套地址转换方式,我们不需要关心。

硬盘的数据寻址有两种方式:CHS、LBA,由于CHS具有一定局限性,目前使用LBA进行寻址。

CHS寻址

CHS寻址通过磁盘的柱面C、磁头H、扇区S来确定数据在哪个扇区

  • 柱面(Cylinder):柱面号指定了数据在哪一个磁道上,从0开始编号,最大1023(用10bit表示)
  • 磁头(Head):磁头编号指定数据在哪一个盘面,从0开始编号,最大255(用8bit表示)
  • 扇区(Sector):扇区号确定了数据在磁道的哪一个位置上,从1开始编号,最大63(用6bit表示)

操作系统通过以上三个编号告诉磁盘数据的位置,一个CHS地址占用3bytes,CHS寻址最多能表示256*1024*64=16777216个扇区,每个扇区512B,因此最大寻址范围约为8GB

CHS寻址无法表示超过8GB的地址空间,因此使用较少或基本不在使用,LBA寻址为了解决CHS寻址范围不够而诞生。

LBA寻址

LBA寻址(Logical Block Addressing,逻辑块寻址)将硬盘的扇区进行线性编号,第一个扇区是LBA 0,第二个是LBA 1,第三个是LBA 2,以此类推,直到最后一个扇区。LBA地址使用48bit来存储编号,理论上能寻址2^48*512KB~=128PB的空间,完全够用。

CHS地址表示了数据的物理地址,而LBA表示数据的逻辑地址。磁盘驱动器在实际操作数据时需要将LBA地址转换为CHS地址。LBA和CHS有一定的转换关系:

LBA=(CH+H)S+(S1)LBA = (C * H + H) * S + (S - 1)

有了LBA寻址后操作系统不在关心柱面和磁头以及实际扇区号CHS(数据的物理地址),而是通过逻辑上的扇区号LBA索引数据(数据的逻辑地址),数据的实际物理位置则由磁盘驱动器负责查找。注意,磁盘驱动器是位于磁盘内部的硬件模块,不是操作系统里的驱动软件。

磁盘驱动器上的CHS就没有限制了吗?

由于操作系统只关心LBA地址(48bit),操作系统中的地址大小是固定的,而在磁盘驱动器上的CHS地址大小就可以随意定义,使用超过24bit的CHS地址就不会有8GB的限制

固态硬盘与4k对齐

固态硬盘不能使用CHS寻址,但固态硬盘有物理扇区划分,每个物理扇区通常为4096字节。使用LBA寻址时将硬盘划分为多个512字节的逻辑扇区。逻辑扇区与物理扇区的转换由硬盘本身来维护,这样操作系统对机械硬盘和固态硬盘就有了统一的寻址方式,而无需关心数据的实际物理位置。

由于固态硬盘的物理扇区(数据最小操作单元)为4096字节,而又因为操作系统一次读取多个扇区(Windows中称为一簇),读写一簇时可能会跨两个物理扇区从而降低硬盘性能,因此一般将分区起始扇区与硬盘的物理扇区对齐。又由于物理扇区是4kB,所以叫4k对齐

硬盘分区

硬盘通常需要分区,就像一间房屋被分为三室两厅。有两种分区模式:传统MBR分区现代GPT分区,MBR分区因为最大只支持2TB硬盘和最大4个分区已不再使用。

分区与格式化

分区是对一整块硬盘而言的分区,仅仅是将硬盘划分出多个区域。格式化是按指定文件系统的格式信息对某个分区进行初始化

传统MBR分区

MBR英文全称为Master Boot Record(主引导记录),主要用来存放启动引导程序和分区表

传统MBR分区结构如图:

MBR分区表存放于硬盘第一个扇区中,占用512B

其中,前446B存放的是启动引导程序MBR(比如Linux上常见的GRUB),此外,每个分区的第一个扇区也可以用来存放引导程序,作为引导扇区。

系统启动流程

1
2
3
4
5
6
7
8
flowchart LR
A[BIOS开机自检] --> B[加载并运行<br>MBR引导程序(第一棒)]
B --> C{查找活动分区}
C -- 找到 --> D[加载活动分区<br>引导扇区(第二棒)]
C -- 未找到 --> E[报错或进入BIOS]
D --> F[运行更强大的<br>操作系统引导程序(第三棒)]
F --> G[加载操作系统内核(第四棒)]
G --> H[启动完成]

后64B是主分区表DPT(Disk Partition Table),记录了4个分区表项。最后两字节作为结束标志或引导签名,固定为0xAA55。

分区表项描述了一个分区的引导标志、分区类型、起止位置、扇区数等信息。

  • 第1个字节引导标志,表示该分区是否为可引导分区(活动分区),若可引导则MBR会尝试加载分区内的引导程序;
  • 2~4字节为分区的第一个扇区的CHS地址,描述分区的起始位置;
  • 第5字节表示该分区的类型,一般用于表示该分区是主分区还是扩展分区(详细类型可看wikipedia),
  • 6~8字节表示分区最后一个扇区的CHS地址,通过起始和结束地址就完整描述了分区位置和大小;
  • 9~12字节表示分区起始扇区的LBA地址
  • 13-16字节表示分区内的扇区数量

扩展分区是为了弥补MBR分区只能有4个分区的不足而产生,扩展分区内的第一个扇区保存了扩展扇区表EBR(Extended Boot Record),其中的446B引导记录为空,仅使用64B扩展分区表中4个分区表项的前两个分区表项,第一个表项描述扩展分区中的逻辑分区的信息,第二个表项描述下一个扩展分区表的扇区位置(若没有下一个扩展分区表则为空)。

现代GPT分区

GPT为全局唯一标识分区表(GUID Partition Table),GUID为硬盘和分区提供了一个唯一的ID。GPT分区模式是源自EFI标准的一种较新的磁盘分区表结构的标准,用来替代BIOS中的主引导记录分区表。

分区结构图示:

保护性MBR(LBA0)就是传统MBR分区表,GPT分区模式为了兼容仅支持MBR分区的系统,在第一个扇区存储了MBR分区表,其中前446字节存储主引导程序,第一个分区表项将整个硬盘划为一个分区类型为保护分区(0x55)的分区,另外三个分区表项为空。不支持GPT分区的系统会将整块硬盘识别为未知分区并且拒绝对硬盘的任何操作,以防止意外删除数据。

GPT分区表头(LBA1)用于存储GPT分区表的位置大小等信息,表头保存的是是硬盘和分区表的元数据。

每一个项的详细说明如下表

起始字节 长度 字段名称 内容
0 8字节 标识符 固定为字符串"EFI PART"(16进制为45 46 49 20 50 41 52 54),用于标识GPT头
8 4字节 版本号 GPT版本号(在1.0版中,值是 00 00 01 00)
12 4字节 分区表头大小 分区表头的大小(单位是字节,通常是92字节,即 5C 00 00 00)
16 4字节 分区表头CRC校验 分区表头(第0-91字节)的CRC32 校验,在计算时,把这个字段作为0处理,需要计算出分区串行的CRC32校验后再计算本字段
20 4字节 保留 保留,必须是 0
24 8字节 表头LBA 这个分区表头的LBA地址
32 8字节 备份表头LBA 备份分区表头的LBA地址
40 8字节 可分区扇区的起始LBA 第一个可用于分区的LBA(主分区表的最后一个LBA + 1,一般为LBA 34)
48 8字节 可分区扇区的末尾LBA 最后一个可用于分区的LBA(备份分区表的第一个LBA − 1,一般为LBA -34)
56 16字节 硬盘标识符 硬盘的GUID(在类UNIX 系统中也叫UUID)
72 8字节 分区表起始LBA 分区表起始LBA(在主分区表中是2)
80 4字节 分区表项最大数量 分区表项的最大数量,通常为128
84 4字节 分区表项大小 一个分区表项的大小(单位为字节,通常是128)
88 4字节 分区表项CRC校验 对​所有分区表项​​计算得到的CRC32校验和,用于验证分区表项数组的完整性
92 * 保留 保留,剩余的字节必须是0(对于512字节每扇区的硬盘通常长度是420个字节)

GPT分区表保存的是GPT分区表项,每个分区表项128字节,一般使用LBA 2~33共32个扇区,因此最大可以有128个分区表项(即128个分区)。结构如下所示:

起始字节 长度 (字节) 字段名称 详细说明
0x00 16 ​​分区类型GUID​​
(Partition Type GUID)
一个全局唯一的标识符,用于定义​​分区的类型和预期用途​​。例如,EFI系统分区、Microsoft基本数据分区或Linux文件系统都有各自固定的GUID。
0x10 16 ​​分区唯一GUID
(Unique Partition GUID)
为此分区生成的​​全局唯一标识符​​。这个GUID用于唯一标识磁盘上的这个特定分区,类似于分区的“身份证号”。
0x20 8 ​​起始LBA
(Starting LBA)
分区第一个扇区的的​​LBA地址​​。
0x28 8 ​​末尾LBA​​ (Ending LBA) 分区末尾扇区的LBA地址
0x30 8 ​​属性标签​​
(Attribute Bits)
一个​​64位的标志位字段​​,用于定义分区的各种属性(如只读、隐藏、不自动挂载等)。
0x38 72 ​​分区名称​​
(Partition Name)
分区的​​人类可读名称​​,使用UTF-16LE编码,最多可容纳36个字符。这不同于操作系统中显示的卷标。

Refer:
https://blog.csdn.net/gui951753/article/details/79365416
https://blog.csdn.net/u010783226/article/details/106069699
https://blog.csdn.net/caodinke/article/details/105347503
https://www.cnblogs.com/guanyouan/articles/3813959.html