做者簡介 必聖,一個看起來不咋正經但很有技術追求的小夥兒,現任餓了麼高級Python工程師,以前有一段和文件系統「搞曖昧」的經歷。聽說他C++寫的挺好,有多好不得而知。html
MBR和GPT都是硬盤分區表,在硬盤分區表以後纔是咱們平常使用的文件系統好比NTFS,FAT32,EXT等。雖然如今MBR已是一個不經常使用的硬盤分區表了,可是GPT爲了兼容老的硬盤分區表因此在GPT的結構頭部加入了MBR。在下面篇幅中,筆者先簡單的介紹一下MBR,接着再介紹GPT結構。c++
本文中貼出一部分結構體代碼,這些結構體都是筆者從010editor的模板中提取出來修改獲得的。這些結構體方便了編程使用,只要把數據buffer指針強制轉換成結構體指針,就能用這個結構體指針來提取數據,免除了使用代碼位移來讀取數據的麻煩。(儘可能採用1字節結構體的對齊方式,避免沒必要要的麻煩)編程
硬盤首一個扇區存放MBR表,由4個部分組成,分別是主引導記錄(Master Boot Record)、數據區、分區表(包含4個分區項)以及結束標誌。api
在下面表格中展現了這4個部分的偏移位置和簡介:數據結構
下面就是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;
複製代碼
硬盤數據截圖工具
下表是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
unsigned short begsect : 6; 表示前6個bitspa
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;
複製代碼
硬盤數據截圖
MBR存儲數據在4個分區上,這些分區稱爲主分區。分區採用「柱面/磁頭/扇區」標記法,即CHS標記法。前面的結構體中beghead、begsect、begcyl分別表示分區起始的磁頭、扇區和柱面,endhead、endsect、endcyl分別表示分區結束的磁頭、扇區和柱面。每一個扇區的大小是512B,總的扇區數=(
,即
),所以它最多隻能描述8G(
)的磁盤區域。
校稿人注
有使用MBR分區經驗的同窗可能會以爲奇怪,怎麼會只能表示8G? 實際裝系統分區的時候,明明輕輕鬆鬆分出了一個幾百G的分區啊。這是由於,現代的BIOS對超過8G的磁盤使用LBA模式,對於超出的部分,CHS值一般設爲0xFEFFFF,並加以忽略,直接使用Offset 0x08-0x0c的4字節相對值,再進行內部轉換。 可是LBA用relsect(32位)來描述起始扇區號,relsect + numsect表示結束扇區號,因爲都是用32位數尋址致使整個硬盤大小不能超過2TB個數據(
)。
除了這個2TB問題以外,MBR 還有其餘困難。主要困難是4個主分區的限制。要克服這個限制,可能的方法是將一個主分區放到一邊,做爲一個佔位符(稱爲擴展分區),用於容納任意數量的附加分區(稱爲邏輯分區)。
MBR 還有數據完整性問題。它是一個單一數據結構,容易受到誤操做和磁盤故障的損壞。另外,因爲邏輯分區以一種連接表結構定義,若是一個邏輯分區損壞,就會阻止對剩餘的邏輯分區的訪問。
讓咱們先貼上GPT的總體結構圖,讓你們有個總體的印象。
GPT結構圖
GPT結構數據
在GPT頭部有個保護MBR(參考GPT結構圖中的LBA 0:Protective MBR),保護MBR數據以下表。
保護MBR只有第一個分區項有數值,其餘分區項爲空。
第一個分區項的PartitionType(分區類型)也就是前文中的systid(分區類型)爲0xEE
StartSectors(起始扇區)這值爲2就是表示LBA1,也就是GPT結構頭所在的位置。在小於2T的硬盤上SectorsInPartition(總扇區數量)就是前文中numsect(總扇區數量),值是整個磁盤大小,因爲只有4個字節只能表示2TB,因此大於2TB該值會被固定爲FFFFFFFFh
保護MBR第一個分區項把整個GPT當作分區項數據記錄下來,它還有的做用是阻止不能識別GPT分區的磁盤工具試圖對其進行格式化等操做,因此該扇區被稱爲「保護MBR」。實上EFI根本不使用這個分區表。
EFI部分又能夠分爲4個區域:EFI信息區(GPT頭)、分區表、GPT分區、備份區。
EFI信息區(GPT頭):起始於磁盤的LBA1,一般也只佔用這個單一扇區。其做用是定義分區表的位置和大小。GPT頭還包含頭和分區表的校驗和,這樣就能夠及時發現錯誤
分區表:分區表區域包含分區表項。這個區域由GPT頭定義,通常佔用磁盤LBA2~LBA33扇區。分區表中的每一個分區項由起始地址、結束地址、類型值、名字、屬性標誌、GUID值組成。分區表創建後,128位的GUID對系統來講是惟一的
GPT分區:最大的區域,由分配給分區的扇區組成。這個區域的起始和結束地址由GPT頭定義
備份區:備份區域位於磁盤的尾部,包含GPT頭和分區表的備份。它佔用硬盤數據的末尾的33個扇區。其中最後一個扇區用來備份LBA1扇區的EFI信息區(GPT頭),其他的32個扇區用來備份LBA2~LBA33扇區的分區表
如下對EFI信息區(GPT頭)、分區表和備份區分別進行探討。
相對字節偏移量(十六進制) | 字節數 | 說明[整數皆以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頭部數據
分區項結構
相對字節偏移量(十六進制) | 字節數 | 說明[整數皆以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 | 不自動掛載,也就是不自動分配盤符 |
分區項數據
備份分區表和分區頭在硬盤數據的末尾,裏面的數據和結構跟分區表和分區頭內同樣。是頭部的一個備份。
因爲分區數量的增多和分區表中起始扇區和結束扇區存儲位數的增長,GPT它消除了 2TB 這個障礙。GPT沒像MBR 單一數據結構,有備份分區表和CRC,能夠有效防止結構損壞。
本文中使用到的工具: 010Editor 十六進制編輯器
閱讀博客還不過癮?
歡迎你們掃二維碼加入交流羣,討論和博客有關的技術問題,還能夠和博主有更多互動
博客轉載、線下活動及合做等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通 ![]()