GDT的由来:

在Protected Mode下,一个重要的必不可少的数据结构就是GDT(Global Descriptor Table)。
为什么要有GDT?我们首先考虑一下在Real Mode下的编程模型:
在Real Mode下,我们对一个内存地址的访问是通过Segment:Offset的方式来进行的,其中Segment是一个段的Base Address,一个Segment的最大长度是64 KB,这是16-bit系统所能表示的最大长度。而Offset则是相对于此Segment Base Address的偏移量。Base Address+Offset就是一个内存绝对地址。由此,我们可以看出,一个段具备两个因素:Base Address和Limit(段的最大长度),而对一个内存地址的访问,则是需要指出:使用哪个段?以及相对于这个段Base Address的Offset,这个Offset应该小于此段的Limit。当然对于16-bit系统,Limit不要指定,默认为最大长度64KB,而 16-bit的Offset也永远不可能大于此Limit。我们在实际编程的时候,使用16-bit段寄存器CS(Code Segment),DS(Data Segment),SS(Stack Segment)来指定Segment,CPU将段积存器中的数值向左偏移4-bit,放到20-bit的地址线上就成为20-bit的Base Address。
到了Protected Mode,内存的管理模式分为两种,段模式和页模式,其中页模式也是基于段模式的。也就是说,Protected Mode的内存管理模式事实上是:纯段模式和段页式。进一步说,段模式是必不可少的,而页模式则是可选的——如果使用页模式,则是段页式;否则这是纯段模式。
既然是这样,我们就先不去考虑页模式。对于段模式来讲,访问一个内存地址仍然使用Segment:Offset的方式,这是很自然的。由于 Protected Mode运行在32-bit系统上,那么Segment的两个因素:Base Address和Limit也都是32位的。IA-32允许将一个段的Base Address设为32-bit所能表示的任何值(Limit则可以被设为32-bit所能表示的,以2^12为倍数的任何指),而不像Real Mode下,一个段的Base Address只能是16的倍数(因为其低4-bit是通过左移运算得来的,只能为0,从而达到使用16-bit段寄存器表示20-bit Base Address的目的),而一个段的Limit只能为固定值64 KB。另外,Protected Mode,顾名思义,又为段模式提供了保护机制,也就说一个段的描述符需要规定对自身的访问权限(Access)。所以,在Protected Mode下,对一个段的描述则包括3方面因素:【Base Address, Limit, Access】,它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符。这种情况下,如果我们直接通过一个64-bit段描述符来引用一个段的时候,就必须使用一个64-bit长的段寄存器装入这个段描述符。但Intel为了保持向后兼容,将段寄存器仍然规定为16-bit(尽管每个段寄存器事实上有一个64-bit长的不可见部分,但对于程序员来说,段寄存器就是16-bit的),那么很明显,我们无法通过16-bit长度的段寄存器来直接引用64-bit的段描述符。
怎么办?解决的方法就是把这些长度为64-bit的段描述符放入一个数组中,而将段寄存器中的值作为下标索引来间接引用(事实上,是将段寄存器中的高13 -bit的内容作为索引)。这个全局的数组就是GDT。事实上,在GDT中存放的不仅仅是段描述符,还有其它描述符,它们都是64-bit长。GDT是Protected Mode所必须的数据结构,也是唯一的。另外,正像它的名字(Global Descriptor Table)所揭示的,它是全局可见的,对任何一个任务而言都是这样。

GDT的构成:

GDT的结构图如下:(GDT表相当于一个64bit的数组)
1、G:
(1)、G=0时,段限长的20位为实际段限长,最大限长为2^20=1MB
(2)、G=1时,则实际段限长为20位段限长乘以2^12=4KB,最大限长达到4GB

2、D/B:

当描述符指向的是可执行代码段时,这一位叫做D位,D=1使用32位地址和32/8位操作数,D=0使用16位地址和16/8位操作数。如果指向的是向下扩展的数据段,这一位叫做B位,B=1时段的上界为4GB,B=0时段的上界为64KB。如果指向的是堆栈段,这一位叫做B位,B=1使用32位操作数,堆栈指针用ESP,B=0时使用16位操作数,堆栈指针用SP。

3、DPL:特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级

4、type : 类型
(1)、type<8时:数据段
(2)、type>=8时:代码段

GDTR是什么?

GDT可以被放在内存的任何位置,那么当程序员通过段寄存器来引用一个段描述符时,CPU必须知道GDT的入口,也就是基地址放在哪里,所以Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。

LDT是什么?

除了GDT之外,IA-32还允许程序员构建与GDT类似的数据结构,它们被称作LDT(Local Descriptor Table,局部描述符表),但与GDT不同的是,LDT在系统中可以存在多个,并且从LDT的名字可以得知,LDT不是全局可见的,它们只对引用它们的任务可见,每个任务最多可以拥有一个LDT。另外,每一个LDT自身作为一个段存在,它们的段描述符被放在GDT中。
 LDT只是一个可选的数据结构,你完全可以不用它。使用它或许可以带来一些方便性,但同时也带来复杂性,如果你想让你的OS内核保持简洁性,以及可移植性,则最好不要使用它。

LDTR是什么?

IA-32为LDT的入口地址也提供了一个寄存器LDTR,因为在任何时刻只能有一个任务在运行,所以LDT寄存器全局也只需要有一个。如果一个任务拥有自身的LDT,那么当它需要引用自身的LDT时,它需要通过lldt指令将其LDT的段描述符装入此寄存器。lldt指令与lgdt指令不同的时,lgdt指令的操作数是一个32-bit的内存地址,这个内存地址处存放的是一个32-bit GDT的入口地址,以及16-bit的GDT Limit。而lldt指令的操作数是一个16-bit的选择子,这个选择子主要内容是:被装入的LDT的段描述符在GDT中的索引值。
至此,我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图:
例如:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。

1. 首先需要装载LDTR使它指向LDT2:使用指令lldt将Select2装载到LDTR。
2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1C h(二进制为11 1 00b),OFFSET=12345678h。逻辑地址为1C:12345678h。
3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h。
4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)。
由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。

段选择子是什么?

引用GDT和LDT中的段描述符所描述的段,是通过一个16-bit的数据结构来实现的,这个数据结构叫做Segment Selector——段选择子。它的高13位作为被引用的段描述符在GDT/LDT中的下标索引,bit 2用来指定被引用段描述符被放在GDT中还是到LDT中,bit 0和bit 1是RPL——请求特权等级,被用来做保护目的。如图所示:
前面所讨论的装入段寄存器中作为GDT/LDT索引的就是Segment Selector,当需要引用一个内存地址时,使用的仍然是Segment:Offset模式,具体操作是:在相应的段寄存器装入Segment Selector,按照这个Segment Selector可以到GDT或LDT中找到相应的Segment Descriptor,这个Segment Descriptor中记录了此段的Base Address,然后加上Offset,就得到了最后的内存地址。
段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。它的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址,段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。
关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一特权级或级别较低特权级的段。
例如给出逻辑地址:21h:12345678h 转换为线性地址的步骤如下:
(1)、选择子SEL=21h=0000000000100 0 01b 它代表的意思是:选择子的index=4即选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;最后的01代表特权级RPL=1
(2)、OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h

关系:

计算机由实模式进入到保护模式要加载gdt,保护模式下的段寄存器由16位的选择器与64位的段描述符寄存器构成。
段描述符寄存器: 存储段描述符;
选择器:存储段描述符的索引;
原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)16位的段寄存器
GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。
48bit的GDTR的结构:
由GDTR访问GDT是通过“段选择子”(实模式下的段寄存器)来完成的,即:
GDTR(48bit)+Segment Selector(由实模式下的段寄存器充当,16bit)= GDT中的某一项 (64bit)
Tips:
(1)、GDT中的所有描述符(除0项外,包括LDT描述符这种类型的描述符)都指定一个段,GDT中的LDT描述符指定的是一个特殊的段, 这个段中只能存放LDT表,之所以说GDT中一个描述符是一个LDT段的描述符,是因为这个描述符中有属性说明这是一个LDT段.
(2)、
GDTR是一个48位的全局描述符寄存器,高32位存放GDT的基址,低16位存放GDT限长。
LDTR是一个16位的局部描述符寄存器,高13位存放LDT在GDT中的索引值。
GDT类似于一个 “数组” ,“数组元素” 可以是段描述符,也可以是LDT描述符。
例如:
若给定一个逻辑地址是 a:b ,根据逻辑地址的a(段选择符)的T1位确定是选择GDT还是LDT。
a、若是T1位选择GDT,根据GDTR找到GDT的基址,根据a的 3~15位确定它的段描述符X在GDT中的位置(GDTR即基址+a的3-15bit即相对位置):确定段描述符X,再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线性地址。
2)若是T1位选择LDT,根据GDTR找到GDT的基址,根据LDTR的高13位确定它的LDTX描述符在GDT中的位置(GDTR基址+LDTR13bit即相对位置):确定LDTX描述符。LDTX描述符可以确定LDT的基址(LDTX描述符确定LDT表在内存中的起始位置),再根据段选择符a确定的相对位置,可以确定LDT中的私有段描述符Y。接下来同上面的:再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线性地址。

GDT中包含的段描述符X和LDT中包含的私有段描述符Y,所占空间相同。
GDT中包含的段描述符X和GDT中包含的LDT描述符,所占用空间相同。

结论是:LDT不包含在GDT中。GDT中只是包含了LDT描述符(一个指向LDT起始地址的指针)。
本文转载自:《GDT与LDT》、《GDT详解》和linux的GDT和LDT

GDT、GDTR、LDT、LDTR的学习相关推荐

  1. 什么是GDT,LDT,GDTR及LDTR?

    (1)全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置,但CPU必须知道GDT的 ...

  2. 对linux内核中GDT和LDT的理解

    GDT(Global Descriptor Table) 在实模式下当我们需要对一个内存地址进行访问的时候,使用的是 [段基地址:偏移地址],这样计算出来的地址就是内存的实际地址.但是到了保护模式,内 ...

  3. 【2021.03.10】段描述符与段选择子、GDT、LDT

    要点回顾 前文提到,当写入一个段寄存器的时候,只给了一个16位的数值,但是段寄存器有96位. 那么剩下的80位从哪里来的?这个16位的数值是随便写的吗? Windbg指令 指令 功能 r 查看寄存器 ...

  4. L2 对GDT与LDT的理解

    L2 对GDT与LDT的理解 1 内存寻址 1.1 实模式下的内存寻址 1.2 保护模式下分段机制的内存寻址 2 什么是GDT 3 如何找到GDT 4 什么是LDT 5 段选择子 6 再看保护模式下分 ...

  5. GDT与LDT的区别

    GDT(Global Descriptor Table)和LDT(Local Descriptor Table)是在x86架构中使用的两种描述符表,用于定义内存段的属性和访问权限.它们之间的主要区别在 ...

  6. 任务寄存器TR:GDT、LDT、IDT、TR、TSS之间的关系

    摘自<Intel® 64 and IA-32 Architectures Software Developer's Manual Combined Volumes1, 2A, 2B, 2C, 2 ...

  7. GDT,LDT,GDTR,LDTR 详解,包你理解透彻

    一.引入 保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成 段描述符寄存器: 存储段描述符 选择器:存储段描述符的索引 段寄存器 PS:原先实模式下的各个段寄存器作为保护模式 ...

  8. GDT、GDTR、LDT、LDTR的理解

    GDT是全局描述附表,主要存放操作系统和各任务公用的描述符,如公用的数据和代码段描述符.各任务的TSS描述符和LDT描述符.(TSS是任务状态段,存放各个任务私有运行状态信息描述符) LDT是局部描述 ...

  9. GDT、GDTR、LDT、LDTR的理解 [zz]

    GDT是全局描述附表,主要存放操作系统和各任务公用的描述符,如公用的数据和代码段描述符.各任务的TSS描述符和LDT描述符.(TSS是任务状态段,存放各个任务私有运行状态信息描述符) LDT是局部描述 ...

最新文章

  1. leetcode网学习笔记(1)
  2. sklearn使用投票回归VotingRegressor算法构建多模型融合的投票回归模型、并自定义子回归器的权重(weights)、评估多模型融合的回归模型、评估R2、mse、rmse、mape
  3. linux shell中 if else以及大于、小于、等于逻辑表达式
  4. Freebsd10.3(FreeBSD11 Beta1)使用手记
  5. 数据结构(2) -- 线性表的顺序表示
  6. java中使用es的dsl_基于DSL的基于图论的Java程序中输入图数据的方法
  7. HTML边框百分比,CSS:以百分比和边框表示的宽度
  8. 数据分析学习笔记1---zip(),numpy.where
  9. 半夜偷看“不良网站”,删除历史记录也没用,“坏影响”已悄然发生
  10. wex5 导入mysql_wex5 sqllite本地数据库的运用
  11. Oracle + PlSql 下载安装配置
  12. 深入理解MessageQueue
  13. Shashlik Cooking(思维)
  14. 如何自学3D游戏场景建模和设计?
  15. 什么是含源一端口网络_u1s1什么意思?网络热词有一说一,u1s1口头禅(用在开头)
  16. Ora2Pg配置文件详解
  17. Microsoft Visio 2010 - 编辑属性值
  18. 手把手教你怎样将win10普通版本升级到最强悍版本win10专业工作站版本
  19. 神奇的伊娃(eval),魔鬼的伊娃(eval)
  20. 项目1在线交流平台-7.构建安全高效的企业服务-3. Security整合Kafka,ES,Thymeleaf实例-对帖子置顶、加精、删除

热门文章

  1. ZOJ3715 竞选班长求最小花费
  2. 【Linux 内核 内存管理】优化内存屏障 ② ( 内存屏障 | 编译器屏障 | 处理器内存屏障 | 内存映射 I/O 写屏障 )
  3. 【Groovy】编译时元编程 ( ASTTransformation#visit 方法中访问 Groovy 类、方法、字段、属性 | 完整代码示例及进行编译时处理的编译过程 )
  4. 【Groovy】map 集合 ( 根据 Key 获取 map 集合中对应的值 | map.Key 方式 | map.‘Key’ 方式 | map[‘Key’] 方式 | 代码示例 )
  5. 【Android 安装包优化】WebP 图片格式性能测试 ( 测试 WebP 图片解码速度 | 测试 WebP 图片编码速度 )
  6. 【Android 高性能音频】Oboe 函数库简介 ( Oboe 简介 | Oboe 特点 | Oboe 编译工具 | Oboe 相关文档 | Oboe 测试工具 )
  7. 【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )
  8. Shell脚本中command not found报错处理
  9. 前端开发之JavaScript基础篇四
  10. 2017-2018-1 20155207 《信息安全系统设计基础》第四周学习总结