Linux GPT分区表16进制实例分析

GPT分区表随着win10的普及,已经在越来越多的新电脑上开始使用了。前段时间的新闻有看到说Intel会在后面的新平台中完全取消CSM支持,这也大概相当于后面新出的Intel平台的主板将只有纯UEFI模式引导,Legacy模式没有了,也就是说必须要使用GPT分区了。

对于普通用户来说,GPT的好处最明显的地方就是支持系统分区大于 2TB,但这似乎并没有什么用(因为MBR并不支持单个分区大于2TB)。另外一个就是,GPT分区必须要用UEFI,而M$从win8开始搞了个SecureBoot,也能从一定程度上增加那么一丢丢安全性吧。然而在服务器领域就不一样了,服务器的SAN存储很容易就能聚合出一个大于 2T的盘,甚至10多T一个LUN都是很常见的事情,尽管Linux下面有LVM可以绕过分区,但是作为未来的主流分区技术,GPT也将会变得很重要,还是有必要来从底层研究一下。

这篇文章我们使用的实验环境是这样的,VMWare虚拟机安装RHEL6.8,除开系统的虚拟硬盘VMDK之外,额外添加两块虚拟硬盘,一块5GB为/dev/sdb,另一块6GB为/dev/sdc。通过解析这两块虚拟硬盘的分区表头的信息,结合GPT的官方手册,用例子来分析GPT分区表到底是怎么构成的。需要说明的是,这篇文章我们的实验只对这两块虚拟硬盘作建立GPT分区表的操作,为避免干扰,先不建立分区。

关于GPT分区表的结构,最权威的是UEFI论坛的Specifications的PDF文档,这个文档可以在http://www.uefi.org/specifications
这里下载到,最新的是UEFI Specification
Version 2.7 (Errata A) 这个版本。另外,这篇文章还参考了维基百科Wikipedia的英文版的GUID分区表的页面:https://en.wikipedia.org/wiki/GUID_Partition_Table

-----------------------------------------------------------------------------

我们使用Linux自带的hexdump工具来从16进制的层面分析GPT分区表。

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

从上面的图中可以看到,我们的两块硬盘/dev/sdb和/dev/sdc分别为5GB和6GB,此时是刚添加了一块新的虚拟硬盘的状态,所以没有分区表,也没有分区,我们使用hexdump来读取这两块硬盘,可以看到里面的数据全都是0,这也符合是一块全新的硬盘的状态。

先来解释一下hexdump的这个输出结果。每一行表示16Byte(字节)的数据,所以第一列的值就显示的是当前行是第多少个字节,它是以16进制显示的。中间那个星号(*)表示它代替的行的数据跟上一行是一样的(这将使得输出结果更易读)。那么红框中140000000和180000000的意思就很明显了,通过将16进制转换成10进制,140000000(Hex) = 5368709120(Dec),180000000(Hex) =
6442450944(Dec),5368709120/1024/1024/1024=5,6442450944/1024/1024/1024=6,结果刚好就是5GB和6GB,这也印证了上面说的第一列的数字表示的是当前行是第多少个字节。140000000和180000000是最后一行,也就是硬盘能存储数据的最后的位置,也就表示硬盘的大小了。

-----------------------------------------------------------------------------

然后我们用parted工具,将这sdb和sdc建立一个gpt分区表。

parted /dev/sdb mklabel gpt & parted /dev/sdc mklabel
gpt

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

分完区之后,再次使用hexdump读取硬盘,就有了上面截图的内容,可以发现,多了不少内容。

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

先来看GPT硬盘整个的硬盘结构示意图,这个图是来自UEFI Specifications文档,LBA0是Protective
MBR,然后就是GPT分区表头,再接着就是每个分区的描述,这三部分构成了主GPT分区表。从First useable block到Last useable block是给每个分区用于存储数据的。最后还有一个备份的GPT分区表,是由每个分区的描述和分区表头构成。

根据UEFI
Specifications文档,GPT分区表头是有两部分的,第一部分一般位于LBA1,另外还有一份alternate GPT,可以理解为备份的GPT表头,它位于硬盘的最后一个LBA。至于LBA是怎么计算的,这里就不展开讲,一般来说,硬盘每个LBA是512Byte字节,所以LBA0就是从0x00000000 到 0x00000200-1,而LBA1就是从0x00000200开始,用进制转换一下,200(Hex) = 512(Dec),就是512字节。

LBA0的Protective
MBR部分这里就不讲了,有兴趣的可以去看官方文档,这篇文章只关心GPT分区表的结构。

GPT的分区表头,是从LBA1开始的,长度一般为92字节,结合UEFI
Specifications和Wikipedia维基百科,根据hexdump的输出结果,我们来一行一行地分析一下sdb的GPT分区表主表头。

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

00000200  45
46 49 20 50 41 52 54  00 00 01 00 5c 00 00
00  |EFI PART....\...|

0-8字节:GPT Signature,它是一串ASCII码字符串“EFI PART”,把这8个16进制数45 46 49 20
50 41 52 54分别转换成10进制,就是69 70 73
32 80 65 82 84,去查一下ASCII码表,它就是字符串“EFI PART”,注意两个单词中间还有个空格。

9-11字节:GPT修订版本,表示是1.0版,即00 00 01
00。

12-15字节:GPT分区表头大小,一般是92字节,也就是16进制数5c,即5c 00 00
00。

00000210  47
f1 8d 83 00 00 00 00  01 00 00 00 00 00 00
00  |G...............|

16-19字节:GPT头部共92字节数据的CRC32校验值,计算该CRC32值时把这4个字节先置为0再计算,计算完成之后,再将计算出来的值填入这4个字节。我们来计算一下。先把hexdump输出结果的92个字节摘出来,再把16-19字节全填成0,就是下面这样:

45 46 49 20 50 41 52 54 00 00
01 00 5c 00 00 00

00 00 00 00 00 00 00 00 01 00
00 00 00 00 00 00

ff ff 9f 00 00 00 00 00 22 00
00 00 00 00 00 00

de ff 9f 00 00 00 00 00 dd 25
f8 96 c6 47 56 4f

aa ed c2 48 bd ee 50 7d 02 00
00 00 00 00 00 00

80 00 00 00 80 00 00 00 86 d2
54 ab

然后把这一串16进制数全选复制填入CRC32在线计算网站:https://www.lammertbies.nl/comm/info/crc-calculation.html
注意要在Input
type中选Hex,这个网站的输入框它会自动忽略空格和换行符,点击Calculate所得计算结果如下:

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

可以看到,这个结果跟hexdump显示的16-19字节数据一致,即47
f1 8d 83。

20-23字节:保留位,必须为0。

24-31字节:当前GPT表头所在的LBA,上面说到主GPT表一般是LBA1,所以结果是01,即01 00 00 00
00 00 00 00。

00000220  ff
ff 9f 00 00 00 00 00  22 00 00 00 00 00 00
00  |........".......|

32-39字节:Alternate GPT表头所在的LBA,上面说到这个一般是最后一个LBA,也就是硬盘的最后一个512字节,这个值是怎么算出来的呢?/dev/sdb容量大小是140000000(Hex) = 5368709120(Dec),除于512字节,5368709120/512 = 10485760(Dec),就是这个硬盘的总LBA数,因为是最后一个LBA所以再减1,即10485760-1 =
10485759(Dec) = 9FFFFF(Hex),

ff ff 9f 00 00 00 00
00

40-47字节:第一个可用的LBA,根据UEFI官方文档规定,LBA大小为512字节时,第一个可用的LBA必须要大于或等于34,即34(Dec) =
22(Hex),即22 00 00 00 00
00 00 00。

00000230  de
ff 9f 00 00 00 00 00  dd 25 f8 96 c6 47 56
4f  |.........%...GVO|

00000240  aa
ed c2 48 bd ee 50 7d  02 00 00 00 00 00 00
00  |...H..P}........|

48-55字节:最后一个可用的LBA,跟上面的第一个可用LBA同样的道理,就是最后一个LBA-34,/dev/sdb容量大小是140000000(Hex) = 5368709120(Dec),5368709120/512 = 10485760(Dec),10485760 - 34 = 10485726(Dec) =
9FFFDE,即de ff 9f 00 00
00 00 00。

56-71字节:硬盘的GUID,这个值是唯一的,不可重复。

72-79字节:定义GPT分区入口的LBA,就是LBA1+1=LBA2,即02
00 00 00 00 00 00 00。

00000250  80
00 00 00 80 00 00 00  86 d2 54 ab 00 00 00
00  |..........T.....|

80-83字节:定义的分区入口的数量,80(Hex) = 128(Dec),128个分区。即80 00 00 00。

84-87字节:每个分区入口的大小,一般是80(Hex) = 128(Dec),即80 00 00 00。

88-92字节:分区入口列表的CRC32校验值。

-----------------------------------------------------------------------------

上面就是主GPT分区表头的16进制逐行分析,下面就是备份分区表头部分了,根据上面的分区,备份的分区表头位置是在硬盘的最后一个LBA,即140000000(Hex) = 5368709120(Dec),5368709120/512 - 1 =
10485759(Dec),10485759 *
512 = 5368708608(Dec) =
13FFFFE00(Hex),即截图中最后一部分的备份GPT分区表头的起码位置。

通过比较主表头和备份表头,我们发现有如下几个地方不一样:

1、16-19字节的GPT头部共92字节数据的CRC32校验值,这个CRC32校验值不一样是由于24-31字节(当前GPT表头所在的LBA)和32-39字节(备份GPT表头所在的LBA)的位置正好是跟主表头位置相反引起的。

2、24-31字节(当前GPT表头所在的LBA)和32-39字节(备份GPT表头所在的LBA),这两个值刚好跟主GPT表头的值是相反的,即互为主备。

3、72-79字节GPT分区入口的LBA,备份表头肯定指向的备份的分区入口列表的LBA。

除了上述3个地方不一样之外,其它地方跟主GPT表头完全一致。

-----------------------------------------------------------------------------

然后我们在/dev/sdb上创建两个分区,再看分区表会有什么样的变化。

[root@RHEL68 ~]# parted
/dev/sdb mkpart sdb1 1 100M

[root@RHEL68 ~]# parted
/dev/sdb mkpart sdb2 100M 300M

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

跟未分区时候相比,多了两个分区入口,这些分区入口在官方文档中被称为GPT Partition Entry
Array。分区表头部分和未分区相比,有两个地方发生了变化,一个是16-19字节的GPT头部共92字节数据的CRC32校验值,这个值发生了变化是因为88-92字节分区入口列表的CRC32校验这个发生了变化,因为多了两个分区,所以分区入口列表的CRC32校验值肯定会发生变化。

第一个分区的入口起始位置是0x400,第二个分区入口起始位置是0x480,所以每个分区入口描述长度是80(Hex) = 128(Dec) Byte。这个128byte其实已经在表头里面有定义过的,上面有说过,就是表头的84-87字节。

每个分区的入口描述根据官方文档是这样定义的,见下图。

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

GPT分区表16进制实例分析" title="Linux GPT分区表16进制实例分析">

下面来分析第一个分区的分区入口描述。

00000400  a2
a0 d0 eb e5 b9 33 44  87 c0 68 b6 b7 26 99
c7  |......3D..h..&..|

0-15字节:分区类型GUID。根据上面提到的维基百科Wikipedia的英文版的GUID分区表的页面:https://en.wikipedia.org/wiki/GUID_Partition_Table
,可以查到这个GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
是指基本数据分区。需要说明的是,各种类型的分区的GUID都是已经有明确定义的,可以参考维基百科页面,里面列了很多常见类型的分区GUID。

00000410  84
b3 41 ef 05 e6 c1 43  8e 61 bb 5b 4f f9 a9
28  |..A....C.a.[O..(|

16-31字节:分区唯一标识符,跟分区表头定义的硬盘GUID一个道理,也必须是唯一值。

00000420  00
08 00 00 00 00 00 00  ff f7 02 00 00 00 00
00  |................|

32-39字节:分区起始LBA,也就是800(Hex) = 2048(Dec),分区真正开始使用是2048*512 = 1048576(Dec) =
100000(Hex)。

40-47字节:分区结束LBA,也就是2F7FF(Hex) = 194559(Dec),分区的最后一个LBA的起始字节数是 194559*512 = 99614208(Dec) =
5EFFE00(Hex),还要加上一个LBA即512字节,即5F00000(Hex),也就是下一个分区的起始字节数。

00000430  00
00 00 00 00 00 00 00  73 00 64 00 62 00 31
00  |........s.d.b.1.|

00000440  00
00 00 00 00 00 00 00  00 00 00 00 00 00 00
00  |................|

48-55字节:属性标识符,保留的。

56-127字节: 分区名称,共72字节。本例中,为0073(Hex),0064(Hex),0062(Hex),0031(Hex),转换到10进制,就是115(Dec),
100(Dec), 98(Dec), 49(Dec),查询一下ASCII码,即sdb1。

Linux GPT分区表16进制实例分析的更多相关文章

  1. linux shell下16进制 “\uxxxx” unicode to UTF-8中文

    问题出现背景: 项目中有个通过ip获取归属地城市需求,我是直接通过新浪的ip归属查询接口来获取的.我使用的是shell脚本调用 RESULT=$(curl -s 'http://int.dpool.s ...

  2. Linux Shell产生16进制随机数

    n为字符长度 openssl rand -hex n

  3. Linux c字符串中不可打印字符转换成16进制

    本文由 www.169it.com 搜集整理 如果一个C字符串中同时包含可打印和不可打印的字符,如果想将这个字符串写入文件,同时方便打开文件查看或者在控制台中打印出来不会出现乱码,那么可以将字符串中的 ...

  4. [转载]Linux 16进制查看命令、工具

    转自:https://blog.csdn.net/chenglian_999/article/details/4672177 2009年10月14日 21:45:00 chenglian_999 阅读 ...

  5. linux以16进制查看文件

    vim 先用vim -b data 以2进制打开文件,然后用xxd工具转化,在vim的命令行模式下: :%!xxd        --将当前文本转化为16进制格式 :%!xxd -r    --将16 ...

  6. linux以16进制方式查看文件

    vim打开文件 :%!xxd  以16进制查看 :%!xxd -r  转回来

  7. Oracle RedoLog-二进制格式分析,文件头,DML,DDL

    上篇文章,简单介绍了 RedoLog 是什么,以及怎么从 Oracle Dump 二进制日志.接下来,分析下 Redo Log 二进制文件的格式,主要包括:文件头,重做日志头,DML-INSERT 操 ...

  8. pyserial 16进制显示与发送

    pyserial 16进制显示与发送 http://www.centoscn.com/python/2013/0817/1320.html 十六进制显示的实质是把接收到的字符诸葛转换成其对应的ASCI ...

  9. 16进制ascii码转化为对应的字符,付ipmitool查询硬件信息

    最近工作需要在用ipmitool查询服务器硬件信息.ipmitool查询硬件信息 比如电源,使用命令: 获取PSU0信息:Ipmitool raw 0x3a 0x71 0x00: 获取PSU1信息:I ...

随机推荐

  1. C#语言基础之第一个C#程序

    1.在记事本中编写如下代码,保存为Simple.cs文件. using System; class Hello World{ public static void Main(){ Console.Wr ...

  2. HDU 1171 Big Event in HDU【01背包】

    题意:给出n个物品的价值和数目,将这一堆物品分给A,B,问怎样分使得两者的价值最接近,且A的要多于B 第一次做的时候,没有思路---@_@ 因为需要A,B两者最后的价值尽可能接近,那么就可以将背包的容 ...

  3. FCC高级编程篇之Record Collection

    Record Collection You are given a JSON object representing a part of your musical album collection. ...

  4. LightOJ-1220 Mysterious Bacteria 唯一分解定理 带条件的最大公因数

    题目链接:https://cn.vjudge.net/problem/LightOJ-1220 题意 给x=y^p,问p最大多少 注意x可能负数 思路 唯一分解定理,求各素因数指数的GCD 注意负数的 ...

  5. [转载]深入JVM锁机制-synchronized

    转自:http://blog.csdn.net/chen77716/article/details/6618779,并加上少量自己的理解 目前在Java中存在两种锁机制:synchronized和Lo ...

  6. BZOJ 2683 简单题 cdq分治+树状数组

    题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后.二维的数据结构是显然不能过的.于是我们可能会考虑把一维排序之后还有一位上数据结构什么的,然而cdq分治却可以非常好 ...

  7. ios xcode真机调试获取屏幕截屏

    非常多时候我们须要在调试的过程中把手机屏幕截图发给其它人看,在android开发中我们能够非常方便的截图保存.而xcode开发ios的时候发现这个需求却如此困难.网上大部分都是介绍的以下这个方案.可是 ...

  8. Android sdk版本以及兼容性问题

    Android:minSdkVersion —— 此属性决定你的应用能兼容的最低的系统版本,一盘情况是必须设置此属性. android:targetSdkVersion —— 此属性说明你当前的应用是 ...

  9. vim 插件之NERD tree

    NERD tree 这个插件可以用来快速浏览目录结构,打开文件 地址 http://www.vim.org/scripts/script.php?script_id=1658 https://gith ...

  10. php如何实现简繁体互转

    php如何实现简繁体互转 一.总结 一句话总结:大部分字是一样的,只转不同的即可 具体转换就是在映射表中找对应的即可 1.简繁体转换的常见问题是什么? 能否智能转换,就是词组, 例如:简体“ 皇后 ” ...