x86中内存段和段描述符

看本文章的朋友。
请支持一次我个人写的搜索页面    
“速谷歌”“ http://www.sugoogle.com

谢谢!!


一、段

保护模式中80x86 提供了4GB的物理地址空间。这是处理器在其地址总线上可以寻址的地址空间。这个地址空间是平坦的,地址范围从0到0xFFFFFFFF。这个物理地址空间可以映射到读写内存、只读内存以及内存映射I/O中。分段机制就是把虚拟地址空间中的虚拟内存组织成一些长度可变的称为段的内存块单元。80386虚拟地址空间中的虚拟地址(逻辑地址)由一个段部分和一个偏移部分构成。段是虚拟地址到线性地址转换机制的基础。每个段由以下几个参数定义:

(1)段基地址(Base Address):指定段在线性地址空间中的开始地址。基地址是线性地址,对应于段中偏移0处。

(2)段限长(Limit):是虚拟地址空间中段内最大可用偏移位置。它定义了段的长度。

(3)段属性(Attributes):指定段的特性。例如该段是否可读、可写或可作为一个程序执行;段的特权级等。

段限长定义了在虚拟地址空间中段的大小。段基址和段限长定义了段所映射的线性地址范围或区域。段内0到limit的地址范围对应线性地址中范围Base到Base+Limit。偏移量大于段限长的虚拟地址是无意义的,如果使用则会导致异常。另外,若访问一个段并没有得到段属性许可则也会导致异常。例如,如果你试图写一个只读的段,那么80386就会产生一个异常。另外,多个段映射到线性地址中的范围可以部分重叠或覆盖,甚至完全重叠,如图4-6所示。在本书介绍的Linux 0.1x系统中,一个任务的代码段和数据段的段限长相同,并被映射到线性地址完全相同而重叠的区域上。

 
图4-6  虚拟(逻辑)地址空间中的段映射到线性地址空间

段的基地址、段限长以及段的保护属性存储在一个称为段描述符(Segment Descriptor)的结构项中。在逻辑地址到线性地址的转换映射过程中会使用这个段描述符。段描述符保存在内存中的段描述符表(Descriptor Table)中。段描述符表是包含段描述符项的一个简单数组。前面介绍的段选择符即用于通过指定表中一个段描述符的位置来指定相应的段。

即使利用段的最小功能,使用逻辑地址也能访问处理器地址空间中的每个字节。逻辑地址由16位的段选择符和32位的偏移量组成,如图4-7所示。段选择符指定字节所在的段,而偏移量指定该字节在段中相对于段基地址的位置。处理器会把每个逻辑地址转换成线性地址。线性地址是处理器线性地址空间中的32位地址。与物理地址空间类似,线性地址空间也是平坦的4GB地址空间,地址范围从0到0xFFFFFFFF。线性地址空间中含有为系统定义的所有段和系统表。

 
图4-7  逻辑地址到线性地址的变换过程

为了把逻辑地址转换成一个线性地址,处理器会执行以下操作:

(1)使用段选择符中的偏移值(段索引)在GDT或LDT表中定位相应的段描述符(仅当一个新的段选择符加载到段寄存器中时才需要这一步)。

(2)利用段描述符检验段的访问权限和范围,以确保该段是可访问的并且偏移量位于段界限内。

(3)把段描述符中取得的段基地址加到偏移量上,最后形成一个线性地址。

如果没有开启分页,那么处理器直接把线性地址映射到物理地址,即线性地址被送到处理器地址总线上。如果对线性地址空间进行了分页处理,那么就会使用二级地址转换把线性地址转换成物理地址。页转换将在稍后进行说明。

二、段描述符

段描述符表是段描述符的一个数组,如图4-8所示。描述符表的长度可变,最多可以包含8192个8字节描述符。有两种描述符表:全局描述符表GDT(Global Descriptor Table)和局部描述符表LDT(Local Descriptor Table)。

 
图4-8  段描述符表结构

描述符表存储在由操作系统维护着的特殊数据结构中,并且由处理器的内存管理硬件来引用。这些特殊结构应该保存在仅由操作系统软件访问的受保护的内存区域中,以防止应用程序修改其中的地址转换信息。虚拟地址空间被分割成大小相等的两半。一半由GDT来映射变换到线性地址,另一半则由LDT来映射。整个虚拟地址空间共含有214个段:一半空间(即213个段)是由GDT映射的全局虚拟地址空间,另一半是由LDT映射的局部虚拟地址空间。通过指定一个描述符表(GDT或LDT)以及表中描述符号,我们就可以定位一个描述符。

当发生任务切换时,LDT会更换成新任务的LDT,但是GDT并不会改变。因此,GDT所映射的一半虚拟地址空间是系统中所有任务共有的,但是LDT所映射的另一半则在任务切换时被改变。系统中所有任务共享的段由GDT来映射。这样的段通常包括含有操作系统的段以及所有任务各自的包含LDT的特殊段。LDT段可以想象成属于操作系统的数据。

图4-9表明一个任务中的段如何能在GDT和LDT之间分开。图中共有6个段,分别用于两个应用程序(A和B)以及操作系统。系统中每个应用程序对应一个任务,并且每个任务有自己的LDT。应用程序A在任务A中运行,拥有LDTA,用来映射段CodeA和DataA。类似地,应用程序B在任务B中运行,使用LDTB来映射CodeB和DataB段。包含操作系统内核的两个段CodeOS和DataOS使用GDT来映射,这样它们可以被两个任务所共享。两个LDT段:LDTA和LDTB也使用GDT来映射。

 
图4-9  任务所用的段类型

当任务A在运行时,可访问的段包括LDTA映射的CodeA和DataA段,加上GDT映射的操作系统的段CodeOS和DataOS。当任务B在运行时,可访问的段包括LDTB映射的CodeB和DataB段,加上GDT映射的段。

这个例子通过让每个任务使用不同的LDT,演示了虚拟地址空间如何能够被组织成隔离每个任务。当任务A在运行时,任务B的段不是虚拟地址空间的部分,因此任务A没有办法访问任务B的内存。同样地,当任务B运行时,任务A的段也不能被寻址。这种使用LDT来隔离每个应用程序任务的方法,正是关键保护需求之一。

每个系统必须定义一个GDT,并可用于系统中所有程序或任务。另外,可选定义一个或多个LDT。例如,可以为每个运行任务定义一个LDT,或者某些或所有任务共享一个LDT。

GDT本身并不是一个段,而是线性地址空间中的一个数据结构。GDT的基线性地址和长度值必须加载进GDTR寄存器中。GDT的基地址应该进行内存8字节对齐,以得到最佳处理器性能。GDT的限长以字节为单位。与段类似,限长值加上基地址可得到最后表中最后1字节的有效地址。限长为0表示有1个有效字节。因为段描述符总是8字节长,因此GDT的限长值应该设置成总是8的倍数减1(即8n-1)。

处理器并不使用GDT中的第1个描述符。把这个"空描述符"的段选择符加载进一个数据段寄存器(DS、ES、FS或GS)并不会产生一个异常,但是若使用这些加载了空描述符的段选择符访问内存时就肯定会产生一般保护性异常。通过使用这个段选择符初始化段寄存器,那么意外引用未使用的段寄存器肯定会产生一个异常。

LDT表存放在LDT类型的系统段中。此时GDT必须含有LDT的段描述符。如果系统支持多LDT的话,那么每个LDT都必须在GDT中有一个段描述符和段选择符。一个LDT的段描述符可以存放在GDT表的任何地方。

访问LDT需使用其段选择符。为了在访问LDT时减少地址转换次数,LDT的段选择符、基地址、段限长以及访问权限需要存放在LDTR寄存器中。

当保存GDTR寄存器内容时(使用SGDT指令),一个48位的"伪描述符"被存储在内存中。为了在用户模式(特权级3)避免对齐检查出错,伪描述符应该存放在一个奇字地址处(即 地址 MOD 4 = 2)。这会让处理器先存放一个对齐的字,随后是一个对齐的双字(4字节对齐处)。用户模式程序通常不会保存伪描述符,但是可以通过使用这种对齐方式来避免产生一个对齐检查出错的可能性。当使用SIDT指令保存IDTR寄存器内容时也需要使用同样的对齐方式。然而,当保存LDTR或任务寄存器(分别使用SLTR或STR指令)时,伪描述符应该存放在双字对齐的地址处(即 地址 MOD 4 = 0)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值