分区表详解





5.00/5 (13投票s)
分区表格式及图形化演示工具

引言
我一直对磁盘如何分区以及分区表的样子感到好奇。本文将解释分区表的格式,并提供一个小型工具(分区查看器)来演示我们解释的内容。
本文信息适用于 x86 系统,我不了解其他系统的磁盘分区。
您无需具备磁盘分区方面的先验知识。本文的目的是提供理解磁盘分区所需的知识,因此许多细节留给读者自行探索。
请注意,本文不包含 GPT(GUID 分区表)。
背景
在本节中,我将介绍一些理解本文所需的概念。
当您购买一块具有给定容量的新硬盘时,您能做的就是从第一个地址到最后一个地址读取/写入原始数据。您可以使用可以读取未格式化/未分区磁盘的特殊工具来实现。您读取/写入的数据没有标准的结构,只有您自己设计的结构。
当我们对磁盘进行分区时,我们只是将磁盘空间划分为分区(逻辑边界)。例如,一个 10GB 的磁盘可以(理论上)划分为任意数量的分区,例如 3GB、4GB 和 3GB 的分区。
[======10 GB============] (分区前)
[(3GB===)(4GB====)(3GB===)] (分区后)
实际上,磁盘分区不仅仅是将磁盘空间划分为更小的分区,还为每个分区附加给定的结构。
这些结构被称为“文件系统”。您可能听说过 FAT 和 NTFS 文件系统(由 Microsoft 操作系统支持)。
文件系统是一种允许您以有组织的方式存储数据(可由主机操作系统管理)的方法。这就是为什么磁盘分区有规则和限制(稍后讨论)。例如,您可以创建一个 10GB 的分区,但不能为其附加 FAT16 文件系统,因为 FAT16 只能管理高达 2GB 的磁盘空间。每种文件系统都有其优缺点,本文档不予讨论。
为了最终确定磁盘分区和文件系统的关系,您可以想象一个原始的、未分区的磁盘可以与一个没有隔间的大房间进行比较,每个员工在这里随意放置物品,但当我们用隔间划分(分区)空间时,我们就有了有组织的办公室和房间(文件系统)。
磁盘寻址和容量限制
在本节中,我们将尝试解释磁盘寻址的概念。
柱面/磁头/扇区 (CHS) 寻址
CHS 代表(C)ylinder/(H)ead/(S)ector 寻址模式。这三个组件共同指定目标磁盘地址。
逻辑块寻址 (LBA)
LBA 是线性寻址模式,从地址 0 开始,直到磁盘中可寻址的最大位置。
与 CHS 寻址相比,LBA 更简单易懂,并且被现代操作系统使用。
MBR 和分区表格式
在本节中,我们将解释 MBR(主引导记录)以及分区表的格式及其在物理磁盘上的位置。
主引导记录 (MBR) 是一个包含引导加载程序和分区表的结构。MBR 始终位于磁盘的最开始处,在 CHS 寻址中是 LBA 0 或 0,0,1。
MBR 结构的大小为 512 字节。
| 偏移量(十六进制) | 大小(字节) | 项目 | 
| 0x0-0x1BD | 446 | 引导加载程序 | 
| 0x1BE-0x1FD | 64 | 分区表 | 
| 0x1FE-0x1FF | 2 | 签名(应为 0x55,0xAA) | 
通常,MBR 的作用是找到哪个分区表是活动的,加载其引导扇区并执行它。
MBR 加载程序可能感染 **MBR 病毒**(有时称为启动程序),该病毒甚至在操作系统启动(或引导扇区执行)之前运行。
引导加载程序
引导加载程序是一个程序,其唯一目的是解释分区表并决定应从哪个分区启动。
引导加载程序可能感染 **引导扇区病毒**,该病毒甚至在操作系统启动之前运行。
以下是引导加载程序通常执行的步骤
- 加载分区表。
- 遍历其条目,查找标记为活动的哪个分区条目。
- 找到活动分区后,加载该分区的相应引导扇区。
- 如果没有找到活动分区,则停止。
- 找到活动分区并加载其相应的引导扇区后,MBR 会执行 MBR 代码。
引导扇区
在上一节中,我们介绍了“引导扇区”一词,这不应与 MBR 的“引导加载程序”混淆。引导扇区和 MBR 是两件不同的事情。
引导扇区是另一个结构,大小为 512 字节,特定于文件系统。其功能是初始化操作系统并从磁盘加载它。由于 MBR 会加载并执行引导扇区,因此引导扇区的最低要求是
- 偏移量 0 应包含有效的 x86 指令,以便当 MBR 执行它时,代码不会导致系统崩溃
- 偏移量 0x1FE 应具有签名 0x55 0xAA(类似于 MBR)
对于 MS-DOS,引导扇区程序负责加载 *IO.SYS* 和 *MS-DOS.SYS*,它们又会加载 *COMMAND.COM*。
分区表
分区表长 64 字节,位于 MBR 的扇区 0x1BE 内。
分区表是 4 个分区表条目的数组,每个条目 16 字节,即:16x4 = 64。
每个分区表条目的格式如下
| 偏移量 | 大小 | 项目 | 
| 0x00 | 1 | 引导指示符;0x80 = 活动分区 / 0x00 非活动分区(*boot_indicator*) | 
| 0x01 | 1 | 分区开始:磁头(*chs_start.head*) | 
| 0x02 | 1 | 分区开始:扇区(*chs_start.sect*) | 
| 0x03 | 1 | 分区开始:柱面(*chs_start.cyl*) | 
| 0x04 | 1 | 分区 ID(例如 ID=1 表示 FAT12)(*system_indicator*) | 
| 0x05 | 1 | 分区结束:磁头(*chs_end.head*) | 
| 0x06 | 1 | 分区结束:扇区(*chs_end.sector*) | 
| 0x07 | 1 | 分区结束:柱面(*chs_end.cyl*) | 
| 0x08 | 4 | 此分区开始之前的扇区数(*sectors_before*) | 
| 0x0C | 4 | 此分区中的扇区数(*number_of_sectors*) | 
您应该注意,开始和结束 CHS 值并非其存储时的值,而是我们需要从位中提取其值。
实际柱面值 = ((chs.sector >> 6) << 8) | (chs.cyl)。我们从 `chs_sector` 中取出位 6-7,它们是 `real_cyl_value` 的高位,然后与 `chs_cyl`(`real_cyl_value` 的低位)结合。
它们被编码成这种方式的原因是,BIOS INT 0x13/AH=0x02 函数(读取扇区)需要这种编码。
INT 13 - DISK - READ SECTOR(S) INTO MEMORY
	AH = 02h
	AL = number of sectors to read (must be nonzero)
	CH = low eight bits of cylinder number
	CL = sector number 1-63 (bits 0-5)
	     high two bits of cylinder (bits 6-7, hard disk only)
	DH = head number
	DL = drive number (bit 7 set for hard disk)
	ES:BX -> data buffer
`number_of_sectors` 字段描述了这个分区中有多少个扇区。此字段可用于确定分区的长度。类似地,我们可以使用(`CHS_END - CHS_START`)来确定分区的长度。
之前的扇区数(偏移量 8 处)指定了从分区表定义开始,分区数据在哪里开始。如果 MBR 中,一个条目的 `sectors_before` 等于 63,则表示该分区的引导扇区位于扇区 63。
主分区和扩展分区
主分区是 ID 不同于 5 和 15 的分区条目,可以关联文件系统。
与主分区不同,扩展分区具有 ID 5 或 15,不能关联文件系统,而是指向另一个分区表,该表最多包含 **两个分区表** 条目。第二个可以是扩展分区,从而在其内部嵌套更多分区)。扩展分区可以被视为其他分区的容器。接下来我们将演示分区表嵌套。
分区表规则
以下分区表规则摘自其他来源,我只列出最相关的(在本文的上下文中)
- 一次只能有一个活动分区。
- 在 MBR 分区表中:最多可以创建 4 个主分区,或者 3 个主分区和 1 个扩展分区。
- 在扩展分区表中:可以有 0 或 1 个扩展分区链接和 0 或 1 个非扩展分区(总共两个)。
- 分区表条目可以按任意顺序(我们可以交换条目 1 和 2 等)。
嵌套分区
由于 MBR 中分区表的结构,我们意识到每个分区表最多只能有 4 个分区表条目。为了克服此限制,引入了特殊的分区表条目,允许我们拥有无限数量的链接分区。
在 MBR 中,扩展分区条目描述了其内部包含的所有分区的总大小(使用 `nb_of_sectors` 字段),而 `NbSectorsBefore` 或 `CHS_START` 将用于计算指向下一个分区表的链接,该表最多可以包含 2 个分区表条目。
这两个条目是
- 一个普通分区条目
- 另一个指向下一个链接分区的扩展分区条目
使用十六进制编辑器手动解释分区表
在本节中,我们将演示如何使用可以显示磁盘扇区的十六进制编辑器手动解释分区表。

此屏幕截图显示了磁盘的第一个扇区。黄色标记的字节是引导加载程序代码。红色标记的字节是 MBR 的签名,应始终为 0x55 0xAA。
绿色区域显示了一个完整的(16 字节)分区表条目
- 00:引导指示符 -> 非活动
- 01 01 00:`CHS_START` -> 磁头 = 1,扇区 = 1,柱面 = 0
- 06:系统指示符 -> FAT16B (>= 32 MB)
- FE 3F 0B:`CHS_END` -> 磁头=254,扇区=63,柱面=11
- 3F 00 00 00:之前的扇区 -> 63
- CD F0 02 00:扇区数 -> 192717
这意味着这是一个大约 96MB 的 FAT16 分区。同样,我们可以解释表的结果并将其读取为
| 条目 # | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 0,1,1 | 6:FAT16B (>= 32 MB) | 11,254,63 | 63 | 192717 | 
| 2 | 0 | 166,0,1 | 15:Win95 扩展 (LBA) | 12543,254,63 | 2666790 | 26748225 | 
| 3 | 0 | 13,0,1 | 23:隐藏的 IFS (HPFS/NTFS) | 154,254,63 | 208845 | 2281230 | 
| 4 | 0 | 12543,0,1 | 28:隐藏的 Win95 FAT32 (LBA) | 12543,254,63 | 29415015 | 4128705 | 
请注意,条目 #2 是一个扩展分区(sysid = 15),大小为:(26748225 * 512) = 13695091200 字节 = 13060 GB。
此分区从扇区 2666790 (`sectors_before`) 开始,让我们去那里看看它指向的分区表
| 条目# | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 166,1,1 | 11:Win95 FAT32 | 178,254,63 | 63 | 208782 | 
| 2 | 0 | 184,0,1 | 5:扩展 | 209,254,63 | 289170 | 417690 | 
正如我们所说,现在从初始扩展分区表条目指向的每个分区表最多有两个条目。第一个条目是普通条目,第二个条目(类型 = 扩展)是另一个分区表的链接。要计算第二个条目的 LBA,我们将 MBR 中扩展分区表条目的“之前的扇区”值添加到“之前的扇区”值中,即:2666790+289170 = 扇区 2955960。
现在我们去那里读取分区表
| 条目# | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 184,1,1 | 6:FAT16B (>= 32 MB) | 209,254,63 | 63 | 417627 | 
| 2 | 0 | 240,0,1 | 5:扩展 | 255,254,255 | 1188810 | 17462655 | 
下一个分区表位于:2666790+1188810 = 扇区 3855600。
| 条目# | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 240,1,1 | 7:可安装文件系统 (NTFS, HPFS) | 255,254,255 | 63 | 17462592 | 
| 2 | 0 | 255,0,193 | 5:扩展 | 255,254,255 | 19069155 | 3919860 | 
下一个分区表位于:2666790+19069155 = 扇区 21735945。
| 条目# | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 255,1,193 | 6:FAT16B (>= 32 MB) | 255,254,255 | 63 | 3919797 | 
| 2 | 0 | 255,0,193 | 5:扩展 | 255,254,255 | 23246055 | 433755 | 
下一个分区表位于:2666790+23246055 = 扇区 25912845。
| 条目# | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 255,1,193 | 130:Linux 交换 / Solaris | 255,254,255 | 63 | 433692 | 
| 2 | 0 | 255,0,193 | 5:扩展 | 255,254,255 | 23679810 | 3068415 | 
下一个分区表位于:2666790+23679810 = 扇区 26346600。
| 条目# | 引导指示符 | CHS_START | 系统 ID | CHS_END | 之前的扇区 | 扇区数 | 
| 1 | 0 | 255,1,193 | 11:Win95 FAT32 | 255,254,255 | 63 | 3068352 | 
| 0 | 0 | 0,0,0 | 0 | 0,0,0 | 0 | 0 | 
没有下一个条目;分区嵌套结束。
一些分区表系统 ID
下表列出了一些已知的系统 ID(或文件系统 ID)。
| ID | 名称 | 
| 0x00 | Empty | 
| 0x01 | FAT12 | 
| 0x02 | XENIX root | 
| 0x03 | XENIX usr | 
| 0x04 | FAT16 <32MB | 
| 0x05 | 扩展 | 
| 0x06 | FAT16B (>= 32 MB) | 
| 0x07 | 可安装文件系统 (NTFS HPFS) | 
| 0x08 | AIX | 
| 0x09 | AIX 可启动 | 
| 0x0A | OS/2 引导管理器 | 
| 0x0B | Win95 FAT32 | 
| 0x0C | Win95 FAT32 (LBA) | 
| 0x0E | Win95 FAT16 (LBA) | 
| 0x0F | Win95 扩展 (LBA) | 
| 0x10 | OPUS | 
| 0x11 | 隐藏的 FAT12 | 
| 0x12 | Compaq diagnostic | 
| 0x14 | 隐藏的 FAT16 <32MB | 
| 0x16 | 隐藏的 FAT16 | 
| 0x17 | 隐藏的 IFS (HPFS/NTFS) | 
| 0x18 | AST SmartSleep | 
| 0x1B | 隐藏的 Win95 FAT32 | 
| 0x1C | 隐藏的 Win95 FAT32 (LBA) | 
| 0x1E | 隐藏的 Win95 FAT16 (LBA) | 
| 0x24 | NEC DOS | 
| 0x2C | WildFile/Adaptec GOBack | 
| 0x39 | 计划 9 | 
| 0x3C | PowerQuest 可恢复分区 | 
| 0x40 | Venix 80286 | 
| 0x41 | PPC PReP 引导 | 
| 0x42 | Veritas 逻辑磁盘管理器 | 
| 0x4d | QNX4.x | 
| 0x4e | QNX4.x 2nd part | 
| 0x4f | QNX4.x 3rd part | 
| 0x50 | OnTrack DM | 
| 0x51 | OnTrack DM6 Aux1 | 
| 0x52 | CP/M | 
| 0x53 | OnTrack DM6 Aux3 | 
| 0x54 | OnTrackDM6 | 
| 0x55 | EZ-Drive | 
| 0x56 | Golden Bow | 
| 0x5c | Priam Edisk | 
| 0x61 | SpeedStor | 
| 0x63 | GNU HURD 或 SysV | 
| 0x64 | Novell Netware 286 | 
| 0x65 | Novell Netware (3.11 和 4.1) | 
| 0x66 | Novell Netware 386 | 
| 0x70 | DiskSecure Multi-Boot | 
| 0x75 | PC/IX | 
| 0x78 | XOSL | 
| 0x80 | Old Minix | 
| 0x81 | Linux/Minix v1.4b+ | 
| 0x82 | Linux 交换 / Solaris | 
| 0x83 | Linux 原生文件系统 (Ext2/3) | 
| 0x84 | OS/2 隐藏类型 04h 分区 | 
| 0x85 | Linux 扩展 | 
| 0x86 | NT FAT 卷集 | 
| 0x87 | NT IFS 卷集 | 
| 0x8e | Linux LVM | 
| 0x93 | Amoeba/隐藏 Linux 原生文件系统 (Ext2/3) | 
| 0x94 | Amoeba BBT | 
| 0x9f | BSD/OS | 
| 0xA0 | IBM Thinkpad 휴면 | 
| 0xA5 | FreeBSD | 
| 0xA6 | OpenBSD | 
| 0xA7 | NeXTSTEP | 
| 0xA9 | NetBSD | 
| 0xB7 | BSDI fs | 
| 0xb8 | BSDI swap | 
| 0xbb | Boot Wizard 隐藏 | 
| 0xc1 | DRDOS / sec (FAT-12) | 
| 0xc4 | DRDOS / sec (FAT-16 < 32M) | 
| 0xc6 | 禁用 NT FAT (FAT-16) 卷集/DRDOS | 
| 0xc7 | Syrinx / 禁用 NT IFS 卷集 | 
| 0xda | 非 FS 数据 | 
| 0xdb | CP/M / CTOS / ... | 
| 0xde | Dell Corporation 诊断分区 | 
| 0xdf | BootIt | 
| 0xe1 | DOS 访问 | 
| 0xe3 | DOS R/O | 
| 0xe4 | SpeedStor | 
| 0xeb | BeOS fs | 
| 0xee | EFI GPT | 
| 0xef | EFI (FAT-12/16/32) | 
| 0xf0 | Linux/PA-RISC 引导 | 
| 0xf1 | SpeedStor | 
| 0xf4 | SpeedStor | 
| 0xf2 | DOS secondary | 
| 0xfd | Linux raid autodetect | 
| 0xfe | LANstep | 
| 0xff | 坏道表 | 
分区查看器工具
为了演示,我编写了一个附带以下组件的工具
- *DiskSector.cpp*/.h - 用于读写原始磁盘扇区的类
- *partitionmanager.cpp*/.h - 用于解析分区表的类
- *MyDrawBar.cpp*/.h - 用于绘制分区表的 MFC 静态组件
- *MyHistory.cpp*/.h - 用于绘制分区表的 MFC 静态组件
- *PartitionViewer[Dlg].cpp*/.h - 使用上述辅助类的 UI
`PartitionViewer` 工具仅硬编码了一小部分系统 ID 和颜色
static const MYHISTORYBARTYPECOLOR part_colors[] = 
{
  {0x0,  RGB(255,255,255), "Free space"},
  {0x6,  RGB(100,10,0), "FAT16B (>= 32 MB)"},
  {0xC,  RGB(255,0,128), "Win95 FAT32 (LBA)"},
  {0xE,  RGB(0,110,110), "Win95 FAT16 (LBA)"},
  {0x7,  RGB(200,0,20), "Installable File System (NTFS, HPFS)"},
  {0x0B, RGB(10,100,0), "Win95 FAT32"},
  {0x83, RGB(20,200,0), "Linux native file system (Ext2/3)"},
  {0x17, RGB(10,0,100), "Hidden IFS (HPFS/NTFS)"},
  {0x82, RGB(101, 12, 130), "Linux swap / Solaris"},
  {0x1C, RGB(90, 127, 30), "Hidden Win95 FAT32 (LBA)"},
  {0x1E, RGB(0, 0, 255), "Hidden Win95 FAT16 (LBA)"},
  {0x04, RGB(101, 12, 130), "FAT16 <32MB"}
};
欢迎您扩展此列表。
此程序需要管理员权限才能运行,因为它会访问磁盘扇区。
执行后

分区将被可视化
- 使用下拉列表选择另一个驱动器,然后按“**表示**”按钮。
- 将鼠标悬停在绘制的区域上,即可在驱动器下拉列表下方的面板中显示分区信息。
结论
希望您觉得这篇文章有用。将来,可以写另一篇文章来介绍 GPT。请在下方留下您的评论或建议,我将尽力在我(有限的)时间和知识范围内回答问题或修复代码。
参考文献
我想感谢所有撰写了使本文成为可能的文章、开源程序和参考资料的人。
- Pierre 的分区表恢复指南 (http://www.datarescue.com/laboratory/partition.htm)
- Boot-Manager Boot-US:术语表 (http://www.boot-us.com/glossary.htm)
- 工作原理:分区表 - Hale Landis 著
- Ralf Brown 中断列表参考
历史
- 2012 年 11 月 2 日:初始版本


