磁盤分區原理:從MBR到GPT

做者簡介 必聖,一個看起來不咋正經但很有技術追求的小夥兒,現任餓了麼高級Python工程師,以前有一段和文件系統「搞曖昧」的經歷。聽說他C++寫的挺好,有多好不得而知。html

引言

MBR和GPT都是硬盤分區表,在硬盤分區表以後纔是咱們平常使用的文件系統好比NTFS,FAT32,EXT等。雖然如今MBR已是一個不經常使用的硬盤分區表了,可是GPT爲了兼容老的硬盤分區表因此在GPT的結構頭部加入了MBR。在下面篇幅中,筆者先簡單的介紹一下MBR,接着再介紹GPT結構。c++

本文中貼出一部分結構體代碼,這些結構體都是筆者從010editor的模板中提取出來修改獲得的。這些結構體方便了編程使用,只要把數據buffer指針強制轉換成結構體指針,就能用這個結構體指針來提取數據,免除了使用代碼位移來讀取數據的麻煩。(儘可能採用1字節結構體的對齊方式,避免沒必要要的麻煩)編程

1.MBR

1.1 MBR的組成

硬盤首一個扇區存放MBR表,由4個部分組成,分別是主引導記錄(Master Boot Record)、數據區、分區表(包含4個分區項)以及結束標誌。api

在下面表格中展現了這4個部分的偏移位置和簡介:數據結構

MasterBoot

  1. Master Boot Record 是硬盤分區表的引導程序和數據區,一共佔用446個字節,BIOS 讀取並執行這段代碼,若是被破壞系統就沒法讀取到硬盤上的數據致使,系統沒法啓動。
  2. 分區項在以後會詳細介紹
  3. 結束標識是MBR的結束位,若是錯誤就會讀取出錯。

下面就是c++中的結構體代碼,bootinst就是主引導程序和數據區、partitions是4個分區項、mbr_table這個類型會在下文詳細介紹,此類型佔據16個字節、signature就是結束表示了。編輯器

typedef struct {
    char    bootinst[446];   /* space to hold actual boot code */
    mbr_table partitions[4];
    unsigned short  signature;/* set to 0xAA55 to indicate PC MBR format */
} mbr_head;
複製代碼

硬盤數據截圖工具

MasterBootRecord

1.2 MBR中的分區項結構

下表是MBR中一個獨立分區項16個字節的解釋編碼

存儲字節位 內容及含義
第1字節 引導標誌。若值爲80H表示活動分區,若值爲00H表示非活動分區。第二、三、4字節 本分區的起始磁頭號、扇區號、柱面號。其中:磁頭號——第2字節;扇區號——第3字節的低6位;柱面號——爲第3字節高2位+第4字節8位。
第5字節 分區類型符。00H——該分區未用(即沒有指定);06H——FAT16基本分區;0BH——FAT32基本分區;05H——擴展分區;07H——NTFS分區;0FH——(LBA模式)擴展分區(83H爲Linux分區等)。
第六、七、8字節 本分區的結束磁頭號、扇區號、柱面號。其中:磁頭號——第6字節;扇區號——第7字節的低6位;柱面號——第7字節的高2位+第8字節。
第九、十、十一、12字節 本分區第一個扇區。
第1三、1四、1五、16字節 本分區的總扇區數。

下面的結構體中的阿拉伯數字是指bit,unsigned short佔用2個字節,一共是16個bites5

  1. unsigned short begsect : 6; 表示前6個bitspa

  2. unsigned short begcyl : 10; 表示以後的10個bit

校稿人注

這裏涉及到C/C++的位域概念,在結構體裏經過 「type [name] : bitsize」 的形式(name是optional的,沒有name的位域稱做無名位域),告訴編譯器僅使用該成員的其中bitsize個比特位存儲數據。當結構體中相鄰兩個成員都是位域、type類型相同且類型長度(bit位數)大於兩個位域bit位數的和,絕大多數編譯器會進行壓縮存儲。 例以下面結構體中的begsect和begcyl,都是位域,type都是unsigned short,且兩個位域的bit位數相加小於unsigned short類型的bit長度,因此這兩個位域會壓縮在一個unsigned short類型的存儲空間內存儲,他們一共佔用2個字節。

typedef struct {
    unsigned char  bootid;         /* bootable? 0=no, 128=yes */
    unsigned char beghead ;    /* beginning head number */
    unsigned short begsect : 6;    /* beginning sector number */
    unsigned short begcyl  : 10;   /* 10 bit nmbr */
    unsigned char  systid;         /* Operating System type indicator code */
    unsigned char endhead ;    /* ending head number */
    unsigned short endsect : 6;    /* ending sector number */
    unsigned short endcyl  : 10;   /* also a 10 bit nmbr */
    unsigned int relsect;          /* first sector relative to start of disk */
    unsigned int numsect;          /* number of sectors in partition */
} mbr_table;
複製代碼

硬盤數據截圖

FdiskPartitioinPartitions

1.3 MBR的限制

MBR存儲數據在4個分區上,這些分區稱爲主分區。分區採用「柱面/磁頭/扇區」標記法,即CHS標記法。前面的結構體中beghead、begsect、begcyl分別表示分區起始的磁頭、扇區和柱面,endhead、endsect、endcyl分別表示分區結束的磁頭、扇區和柱面。每一個扇區的大小是512B,總的扇區數=2^{24}磁頭數\times扇區數\times柱面數,即2^{8}\times2^{6}\times2^{10}),所以它最多隻能描述8G(8G=2^{24}\times512B\div1024\div1024\div1024)的磁盤區域。

校稿人注

有使用MBR分區經驗的同窗可能會以爲奇怪,怎麼會只能表示8G? 實際裝系統分區的時候,明明輕輕鬆鬆分出了一個幾百G的分區啊。這是由於,現代的BIOS對超過8G的磁盤使用LBA模式,對於超出的部分,CHS值一般設爲0xFEFFFF,並加以忽略,直接使用Offset 0x08-0x0c的4字節相對值,再進行內部轉換。 可是LBA用relsect(32位)來描述起始扇區號,relsect + numsect表示結束扇區號,因爲都是用32位數尋址致使整個硬盤大小不能超過2TB個數據(2T=2^{32}\times512B\div1024\div1024\div1024\div1024)。

除了這個2TB問題以外,MBR 還有其餘困難。主要困難是4個主分區的限制。要克服這個限制,可能的方法是將一個主分區放到一邊,做爲一個佔位符(稱爲擴展分區),用於容納任意數量的附加分區(稱爲邏輯分區)。

MBR 還有數據完整性問題。它是一個單一數據結構,容易受到誤操做和磁盤故障的損壞。另外,因爲邏輯分區以一種連接表結構定義,若是一個邏輯分區損壞,就會阻止對剩餘的邏輯分區的訪問。


2.GPT

2.1 GPT的組成

讓咱們先貼上GPT的總體結構圖,讓你們有個總體的印象。

GPT結構圖

GPT結構

GPT結構數據

GPT結構數據

2.2 保護MBR

在GPT頭部有個保護MBR(參考GPT結構圖中的LBA 0:Protective MBR),保護MBR數據以下表。

保護MBR

保護MBR只有第一個分區項有數值,其餘分區項爲空。

  1. 第一個分區項的PartitionType(分區類型)也就是前文中的systid(分區類型)爲0xEE

  2. StartSectors(起始扇區)這值爲2就是表示LBA1,也就是GPT結構頭所在的位置。在小於2T的硬盤上SectorsInPartition(總扇區數量)就是前文中numsect(總扇區數量),值是整個磁盤大小,因爲只有4個字節只能表示2TB,因此大於2TB該值會被固定爲FFFFFFFFh

保護MBR第一個分區項把整個GPT當作分區項數據記錄下來,它還有的做用是阻止不能識別GPT分區的磁盤工具試圖對其進行格式化等操做,因此該扇區被稱爲「保護MBR」。實上EFI根本不使用這個分區表。

2.3 EFI部分

EFI部分又能夠分爲4個區域:EFI信息區(GPT頭)、分區表、GPT分區、備份區。

  1. EFI信息區(GPT頭):起始於磁盤的LBA1,一般也只佔用這個單一扇區。其做用是定義分區表的位置和大小。GPT頭還包含頭和分區表的校驗和,這樣就能夠及時發現錯誤

  2. 分區表:分區表區域包含分區表項。這個區域由GPT頭定義,通常佔用磁盤LBA2~LBA33扇區。分區表中的每一個分區項由起始地址、結束地址、類型值、名字、屬性標誌、GUID值組成。分區表創建後,128位的GUID對系統來講是惟一的

  3. GPT分區:最大的區域,由分配給分區的扇區組成。這個區域的起始和結束地址由GPT頭定義

  4. 備份區:備份區域位於磁盤的尾部,包含GPT頭和分區表的備份。它佔用硬盤數據的末尾的33個扇區。其中最後一個扇區用來備份LBA1扇區的EFI信息區(GPT頭),其他的32個扇區用來備份LBA2~LBA33扇區的分區表

如下對EFI信息區(GPT頭)、分區表和備份區分別進行探討。

2.3.1 EFI信息區數據結構

相對字節偏移量(十六進制) 字節數 說明[整數皆以little endian方式表示]
00~07 8 GPT頭簽名「45 46 49 20 50 41 52 54」(ASCII碼爲「EFI PART」)
08~0B 4 版本號,目前是1.0版,其值是「00 00 01 00」
0C~0F 4 GPT頭的大小(字節數),一般爲「5C 00 00 00」(0x5C),也就是92字節。
10~13 4 GPT頭CRC校驗和(計算時把這個字段自己看作零值)
14~17 4 保留,必須爲「00 00 00 00」
18~1F 8 EFI信息區(GPT頭)的起始扇區號,一般爲「01 00 00 00 00 00 00 00」,也就是LBA1。
20~27 8 EFI信息區(GPT頭)備份位置的扇區號,也就是EFI區域結束扇區號。一般是整個磁盤最末一個扇區。
28~2F 8 GPT分區區域的起始扇區號,一般爲「22 00 00 00 00 00 00 00」(0x22),也便是LBA34。
30~37 8 GPT分區區域的結束扇區號,一般是倒數第34扇區。
38~47 16 磁盤GUID(全局惟一標識符,與UUID是同義詞)
48~4F 8 分區表起始扇區號,一般爲「02 00 00 00 00 00 00 00」(0x02),也就是LBA2。
50~53 4 分區表總項數,一般限定爲「80 00 00 00」(0x80),也就是128個。
54~57 4 每一個分區表項佔用字節數,一般限定爲「80 00 00 00」(0x80),也就是128字節。
58~5B 4 分區表CRC校驗和
5C~ 保留,一般是全零填充

GPT分區項數量能夠根據GPT中的分區表屬性擴展。也就是GPT的分區表數量是能夠配的,可是通常大小都是128個分區項。而MBR只有4個分區項。

typedef struct {
    BYTE   SIGNATURE[8];
    DWORD  Revision;
    DWORD  Headersize;
    DWORD  CRC32OfHeader;
    DWORD  Reserved;
    UINT64 CurrentLBA;
    UINT64 BackupLBA; //location of the other head copy
    UINT64 FirstUsableLBA; //primary partition table last LBA+1
    UINT64 LastUsableLBA; //secondary parition table first LBA-1
    BYTE DiskGUID[16];
    UINT64 PartitionEntries;
    DWORD  NumOfPartitions;
    DWORD  SizeOfPartitionEntry;
    DWORD  CRC32ofPartitionArray;
    BYTE reserved[420];
} gpt_head_table;
複製代碼

GPT頭部數據

Gptheader

2.3.2 分區表

分區項結構

相對字節偏移量(十六進制) 字節數 說明[整數皆以little endian方式表示]
00~0F 16 用GUID表示的分區類型
10~1F 16 用GUID表示的分區惟一標示符
20~27 8 該分區的起始扇區,用LBA值表示。
28~2F 8 該分區的結束扇區(包含),用LBA值表示,一般是奇數。
30~37 8 該分區的屬性標誌
38~7F 72 UTF-16LE編碼的人類可讀的分區名稱,最大32個字符。

分區項中的PartitionStartLBA(起始扇區)和PartitionEndLBA(該分區的結束扇區)都是64位,也就從MBR24位增長到了64位,並且添加CRC的校驗。

typedef struct {
    BYTE PartitionTypeGUID[16];
    BYTE PartitionGUID[16];
    UINT64 PartitionStartLBA;
    UINT64 PartitionEndLBA;
    UINT64 PartitionProperty;
    wchar_t PartitionName[36]; //Unicode
} gpt_paptition_table;
複製代碼

下面表格是PartitionTypeGUID(分區類型GUID)值。

相關操做系統 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)]
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 微軟保留分區
Windows EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 基本數據分區
Windows DE94BBA4-06D1-4D40-A16A-BFD50179D6AC Windows恢復環境
Linux 0FC63DAF-8483-4772-8E79-3D69D8477DE4 數據分區。Linux曾經使用和Windows基本數據分區相同的GUID。這個新的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 3B8F8425-20E0-4F3B-907F-1A25A76F98E8 Server Data (/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 保留

Microsoft還進一步對分區的屬性進行了細分:低位4字節表示與分區類型無關的屬性,高位4字節表示與分區類型有關的屬性。Microsoft目前使用了下列屬性:

Bit 解釋
0 系統分區(磁盤分區工具必須將此分區保持原樣,不得作任何修改)
1 EFI隱藏分區(EFI不可見分區)
2 傳統的BIOS的可引導分區標誌
60 只讀
62 隱藏
63 不自動掛載,也就是不自動分配盤符

分區項數據

分區屬性

2.3.3 備份分區表,備份GPT頭

備份區

備份分區表和分區頭在硬盤數據的末尾,裏面的數據和結構跟分區表和分區頭內同樣。是頭部的一個備份。

2.4 GPT優點

因爲分區數量的增多和分區表中起始扇區和結束扇區存儲位數的增長,GPT它消除了 2TB 這個障礙。GPT沒像MBR 單一數據結構,有備份分區表和CRC,能夠有效防止結構損壞。

工具

本文中使用到的工具: 010Editor 十六進制編輯器




閱讀博客還不過癮?

歡迎你們掃二維碼加入交流羣,討論和博客有關的技術問題,還能夠和博主有更多互動

博客轉載、線下活動及合做等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通
相關文章
相關標籤/搜索