您的位置:首页 > 运维架构 > Linux

linux 内核源代码情景分析——Intel X86 CPU 系列的寻址方式

2013-10-18 10:53 525 查看
当我们说一个CPU是“16位”或“32”位时,指的是处理器中“算数逻辑单元”(ALU)的宽度。数据总线通常与ALU具有相同的宽度。当Intel决定在16位CPU 8086中采用1M字节的内存空间,地址总线的宽度也就确定了,那就是20位。问题就来了:既然是ALU是16位,也就是说直接加以运算的指针长度是16位,即这个指针所能代表的地址最多有216,不能达到20位地址总线所能寻址的最大空间,于是Intel在8086 CPU中设置了4个“段寄存器”:CS、DS、SS和ES,分别用于可执行代码、数据、堆栈和其他。每个段寄存器都是16位的,对应于地址总线中的高16位。每条“访内”指令中的“内部地址”都是16位的,但是在送上地址总线之前都在CPU内部自动的与某个段寄存器的内容相加,形成一个20的实际地址。这样,就实现了从16位内部地址到20位实际地址的转换。转换公式:实际地址 = 段寄存器 + (内部地址<<4)。

由于8086这种到内存寻址方式缺乏对内存空间的保护,所以为了区别于后来的“保护模式”,就称为“实地址模式”。针对8086这种缺陷,Intel从80286开始实现“保护模式”,不久后80386 CPU也开发成功了,从80386以后,虽然在速度和功能上不断的提改进,但并无重大的质的改变,遂统称为i386结构,下面我们将以80386为背景,介绍i386的保护模式。

1、虚拟地址、逻辑地址、线性地址、物理地址的区别



由图可以看出它们之间的区别

2、分段机制



分段步骤:

1)先检查段选择符TI,确定段描述符保存在GDTR还是LDTR中

2)从段选择符高13位(见下图的段选择符格式)* 8 + GDTR中的内容得到段基地址。也可以这样想,GDTR存放的是段描述符表这个数组的首地址,而段选择符的高13位*8是其下标,因为段描述符是8字节长,所以在段描述表里是以8字节为一个单位

3)然后将2)得到的值与偏移量相加就得到了线性地址

3、段选择符格式



GDT的第一项是”空选择符“(即索引值为0,TI标志为0),当把它加载到一个段寄存器(除了CS和SS之外)中时,处理器不会产生异常,但是当使用含有空选择符的段寄存器访问内存时就会产生异常。段选择符的值通常由链接编辑器或链接加载程序进行设置或修改,而非应用程序.

为了减少地址转换时间和复杂性,处理器提供了6个段寄存器,为了访问某个段的程序,必须把段选择符加载到一个段寄存器中,为了避免每次访问内存时都去读、解码一个段描述符和引用段描述符表,每个段寄存器都有一个“可见”部分和“隐藏”部分(影子寄存器),当一个段选择符被加载到一个段寄存器可见部分中时,处理器也同时把段选择符指向的段描述符中的段地址、段限长和访问控制信息加载到段寄存器的隐藏部分中。缓冲在段寄存器(可见和隐藏部分)中的信息时的处理器可以在进行地址转换时不再需要花费时间从段描述符中读取基地址和段限长。操作系统必须保证对描述符表的改动反映到影子寄存器中,最简单的方法是在对描述符表中描述符作过任何改动之后就立刻重新加载6个段寄存器

4、段描述符格式



B31——B24、B23——B16、B15——B0组合成段基地址Base,Base为32位

L19——L16、L15——L0组合成段限长Limit,Limit为20位

G颗粒度,G=0,Limit单位是字节,G=1,Limit单位是4KB

D 如果段偏移量是32位置1,若是16位,清0

AVL 系统软件可用位,忽略

P为0表示不在内存中,linux总是为1

DPL 表示为访问这个段而要求的CPU最小的优先级

S系统标志:0表示是一个系统段,否则是普通的代码段或数据段

type 指定段的类型、说明段的访问种类以及段的扩展方向
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: