分段机制

来源:互联网 发布:域名怎么申请域名邮箱 编辑:程序博客网 时间:2024/06/05 17:04

首先谈一下80X86系统中存在的三个地址概念:逻辑地址、线性地址和物理地址。

逻辑地址:是指由程序产生的与段相关的偏移地址,也就是在Intel保护模式下程序执行代码段限长内的偏移地址。例如,在C语言指针编程中,读取指针变量本身的值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址,与绝对物理地址不相关。应用程序员仅需与逻辑地址打交道,而分段和分页机制对他们来说是透明的,仅由系统编程人员涉及。应用程序员虽然自己可以直接操作内存,但那只是在操作系统给你分配的内存段操作。逻辑地址的表达形式为:段地址:段内偏移地址。

线性地址:是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或者说是段内的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启动了分页机制,那么线性地址可以再变换为一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。

物理地址:是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。虚拟内存(Virtual Memory) 是指计算机呈现出要比实际拥有的内存大得多的内存量。因此它允许程序员编制并运行比实际系统拥有的内存大得多的程序。这使得许多大型项目也能够在具有有限内存资源的系统上实现。一个很恰当的比喻是:你不需要很长的轨道就可以让一列火车从上海开到北京。你只需要足够长的铁轨(比如说3公里)就可以完成这个任务。采取的方法是把后面的铁轨立刻铺到火车的前面,只要你的操作足够快,并能满足要求,列车就能像在一条完整的轨道上运行。这也就是虚拟内存管理需要完成的任务。在Linux 0.11内核中,给每个程序(进程)都划分了总容量为64MB的虚拟内存空间。因此程序的逻辑地址范围是0x0000000到0x4000000。有时我们也把逻辑地址称为虚拟地址。因为与虚拟内存空间的概念类似,逻辑地址也是与实际物理内存容量无关的。 逻辑地址与物理地址的“差距”是0xC0000000,是由于虚拟地址->线性地址->物理地址映射正好差这个值。这个值是由操作系统指定的。

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

为什么会有分段概念?Intel8086是16位CPU,它只有16位寄存器、16位数据总线和20位地址总线,如果程序要想访问大于64K(2^16)的内存,就要把内存分段,每段64K,用段地址+偏移量的方法来访问(物理地址=段值<<4 + 偏移)。 

对于一个20位的物理地址可以对于多个逻辑地址形式。所以物理地址转换成逻辑地址,需要明确段基地址。

例如,逻辑地址1460H:0100H 和 1380H:0900H 对应于同一个物理地址 14700H

段基地址(Segment):逻辑段在主存中的起始位置,简称段地址。由于8086规定段开始于模16地址,所以省略低4位0不显式表达,段基地址就可以用16位数据表示。

偏移地址(Offset):主存单元距离段起始位置的偏移量(Displacement)。由于限定每段不超过64KB(2^16),所以偏移地址也可以用16位数据表示。

 

而从80386开始,CPU有32位地址线,所以寻址空间可以达到4GB,而且所有通用寄存器都是32位,所以用任何一个通用寄存器来间接寻址,不用分段就可以访问4GB空间中的任意内存地址。但这并不意味着,此时段寄存器就不再有用了,实际上,段寄存器更加有用了,虽然再寻址上没有分段的限制了,但在保护模式下,一个地址空间是否可以被写入,可以被多少优先级的代码写入,是不是允许执行等等涉及保护的问题就出来了。要解决这些问题,必须对一个地址空间定义一些安全上的属性。

段寄存器这时就派上了用场。但是设计属性和保护模式下段的参数,要表示的信息太多了,要用64位长的数据才能表示。我们把着64位的属性数据叫做段描述符,上面说过,它包含3个变量:段物理首地址、段界限、段属性,80386的段寄存器是16(注意:通用寄存器在保护模式下都是32位,但段寄存器没有被改变)的,无法放下保护模式下64位的段描述符。

如何解决这个问题呢?方法是把所有段的段描述符顺序存放在内存中的指定位置,组成一个段描述符表(Descriptor Table);而段寄存器中的16位用来做索引信息,这时,段寄存器中的信息不再是段地址了,而是段选择符(Selector)。

可以通过它在段描述符表中“选择”一个项目已得到段的全部信息。那么段描述符表存放在哪里呢?80386引入了两个新的寄存器来管理段描述符,就是GDT和LDT。这样,用以下几步来总体体验下保护模式下寻址的机制

1、段寄存器中存放段选择符Selector 

2、GDT寄存器中存放着段描述符表的首地址 

3、通过选择子根据GDT中的首地址,就能找到对应的段描述符 

4、段描述符中有段的物理首地址,就得到段在内存中的首地址 

5、加上偏移量,就找到在这个段中存放的数据的线性地址(只有分段机制情况下,就是真实物理地址)。 

接上面所述,GDT的作用就是用来提供段式存储机制,这种机制是通过段寄存器和GDT中的描述符共同提供的。

因为要能够访问4GB内存,所以在32位模式下

物理地址不能等于: 段值*16+偏移 (这种方法寻址1MB)

于是,设计者让段值*16指向一个数据结构的一个表项就是GDT(or LDT),表项中详细定义了段的起始地址,界限和属性等内容。于是原先“段:偏移”形式的逻辑地址经过段机制转化成线性地址,如果只有分段机制,那么这个地址就是对应的真实内存的物理地址。

 注:以上内容主要参考赵炯的《linux内核完全注释》和网上一些资料。

0 0
原创粉丝点击