Chinaunix首页 | 论坛 | 博客
  • 博客访问: 52974
  • 博文数量: 32
  • 博客积分: 26
  • 博客等级: 民兵
  • 技术积分: 13
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-01 23:00
文章分类

全部博文(32)

文章存档

2013年(32)

我的朋友

分类: LINUX

2013-11-20 09:43:06

原文地址:内存管理的学习和理解 作者:sun5411

 

Ø  80386中的寄存器

·         432位通用寄存器:EAXEBXECXEDX

·         432位地址寄存器:ESPEBPEDIESI

·         32位指令指针寄存器:EIP

·         32位标志寄存器:EFLAGS

·         616位段寄存器:CS(代码段)DS(数据段)ES(附加段)SS(堆栈段)FSGS

·         432位控制寄存器:CR0CR1CR2CR3

·         4个段表基地址寄存器:GDTR(48)IDTR(48)LDTR(16)TR(16)

·         排错和测试寄存器

 

Ø  实模式与保护模式

·         实模式:寻址采用和8086相同的16位段和偏移量,最大寻址空间1MB,最大段64KB。可以使用32位指令。32位的x86 CPU做高速的8086.

·         保护模式:寻址采用32位段和偏移量,最大寻址空间4GB,最大分段4GBpentium pre及以后为64GB)。在保护模式下CPU可以进入虚拟8086方式,这是在保护模式下的实模式运行环境。

第一:实模式下程序运行回顾。

          程序运行的实质是什么? 其实很简单,就是指令的执行,显然cpu是指令的一致性的硬件保障。

         那么cpu如何知道指令在什么地方呢?对了,80x86系列是使用CS寄存器配合IP寄存器来通知CPU指令在内存中的位置。程序可能需要调用系统的服务子程序,80x86系列使用中断机制来实现系统服务。

        总得来说,这些就是实模式下一个程序运行的主要内容(其他如跳转、返回、端口操作等相对来说比较次要。)

 

第二:保护模式----从道行徐运行说起

         无论是实模式还是 保护模式,根本问题还是程序如何在其中运行。因此我们在学习保护模式的时候应该时刻围绕这个问题来思考。

        和实模式下一样,保护模式下程序运行的实质仍是“cpu执行指令,操作相关数据”,因此是模式下的各种代码段、数据段、堆栈段、中断服务程序仍然存在,且功能和作用不变。

      那么和保护模式下最大的变化是什么呢? 地址转换方式

 

第三: 地址转换方式的比较

         先看一下实模式下地址转换方式,假设我们在ES中存入0x1000DI中存入0xFFFF,那么ESDI=0x1000=0x1000*0x10 + 0xFFFF,这就是众所周知的“左移4位加偏移量”。

那么如果在保护模式下呢?  假设上面的数据不变ES=0x1000DI=0xFFFF,现在ESDI等于什么呢?

         公式如下:(注:0x1000=1000000000000b=10 0000 0000 0 00

        ESDI=全局描述符表中第0x1000项描述符给出的段基地址+0xFFFF

         现在比较一下,好像是不一样,再仔细看,又好像没什么区别!

         为什么说没什么区别,因为既然ES中内容不是真正的段地址,凭什么实模式下称ES为“段寄存器”,而到了保护模式就说是“选择子”?

          其实它们都是一种映射关系,只是映射规则不同而已: 在实模式下这个地址转换方式是“左移4位”,在保护模式下是“查全局/局部描述符表”。前者是系统定义的映射方式,后者是用户自定义的转换方式,而它影响的都是“shadow register”。

保护模式地址转换:

        从函数观点来看,前者是表达式,后者是列举式函数:

实模式: Fesàsegment= {segment|segment = es*0x10}

保护模式: Fesàsegment= {segment|(es,segment) GDT/LDT}

其中GDTLDT分别表示全局描述符表和局部描述符表。

描述符表有三种,分别为GDTLDTIDT(中断描述符表)

1.       全局描述符表GDT

全局描述符表在系统中只能有一个,且可以被每个任务所共享,任何描述符都可以放在GDT中,但中断门和陷阱门放在GDT中是不会起作用的。能被多个任务共享的内存区就是通过GDT完成的。

2.       局部描述符表:

局部描述符表在系统中可以有多个,通常情况下是与任务的数量保持对等,但任务可以没有局部描述符表,任务间不相干的部分也是通过LDT实现的,这里涉及到地址映射的问题,和GDT一样,中断们和陷阱门不会起作用。

3.       中断描述符表IDT

GDT一样,中断描述符表在系统最多只能有一个,中断描述符表内可以存放256个描述符,分别对应256个中断,因为每个描述符占用8个字节,所以IDT的长度可达2k,中断描述符中可以有任务门、中断门、陷阱门三个描述符,其他描述符在中断描述符表中无意义。

4.       段选择子

在保护模式下,段寄存器的内容已不是段值,而成其为选择子。该选择子只是描述符在上面这三个表中的位置,所以说选择子即是索引值。

当我们把选择子装入寄存器时不仅使用该寄存器值,同时CPU将该选择子所对应的GDTLDT中的描述符装入了不可见的部分。这样只要我们不进行代码切换(不重装入新的选择子)CPU就不会对不可见部分存储的描述符进行更新,可以直接进行访问,加快了访问速度。一旦寄存器被重新赋值,不可见部分也将被重新赋值。

为什么LDT要放在GDT中:

    LDT中的描述符和GDT中的描述符除了选择子的bit3一个为0一个为1用于区分该描述符是在GDT中还是在LDT中外,描述符本身的结构完全一样。开始我考虑既然是这样,为什么要将LDT放在GDT中而不是像GDT那样找一个GDTR寄存器呢?

   后来终于明白了原因——很简单,GDT表只有一个,是固定的;而LDT表每个任务就可以有一个,因此有多个,并且由于任务的个数在不断变化其数量也在不断变化。如果只有一个LDTR寄存器显然不能满足多个LDT的要求。因此INTEL的做法是把它放在放在GDT中。

GDTR全局描述符寄存器:48位,高32位存放GDT基址,低16为存放GDT限长。

LDTR局部描述符寄存器:16位,高13为存放LDTGET中的索引值。

IA-32处理器仍然使用xxxxyyyyyyyy(段选择器:偏移量)逻辑方式表示一个线性地址,那么是怎么得到段的基址呢?在上面说明中我们知道,要得到段的基址首先通过段选择器xxxxTI位指定的段描述符所在位置:

TI=0时表示段描述符在GDT中,如下图所示:先从GDTR寄存器中获得GDT基址。然后再GDT中以段选择器高13位位置索引值得到段描述符。段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址。

TI=1时表示段描述符在LDT中,如下图所示:还是先从GDTR寄存器中获得GDT基址。LDTR寄存器中获取LDT所在段的位置索引(LDTR13)以这个位置索引在GDT中得到LDT段描述符从而得到LDT段基址。用段选择器高13位位置索引值从LDT段中得到段描述符。段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址。

 

第四:保护模式的基本组成

        保护模式最近本的组成部分是围绕着“地址转换方式”的变化增设了相应的机构。

1.       数据段

前面说过,实模式下的各种代码段、数据段、堆栈段、中断服务程序仍然存在,我将它们统称为“数据段”,本文从此乡下提到数据段都是使用这个定义。

2.       保护模式下引入描述符来描述各种数据段,所有的描述符均为8个字节(0-7),由第5个字节说明描述符的类型,类型不同,描述符的结构也有所不同。

    若干个描述符集中在一起组成描述符表,而描述符表也是一种数据段,也使用描述 符进行描述。

    从现在起,“地址转换”由描述表来完成,从这个意义上说,描述符表也是一张地址转换的函数表

3.       选择子

选折子是一个2字节的数,共16位,最低2位表示RPL,第3位表示查表是利用GDT(全局描述符表)还是LDT(局部描述符表)进行,最高13位给出了所需的描述符在描述符表中的地址。(注:13位正好足够地址8k项)

有了以上三个概念之后可以进一步工作了,现在程序的运行与实模式下完全一样!!!各段寄存器仍然给出一个“段值”,只是这个“假段值”到真正的段地址不再是“左移4位”,而是利用描述符来完成。但出现一个新的问题是:

      系统如何知道GDT/LDT在内存中的位置呢?

      为了解决这个问题,显然需要引入新的寄存器用于指示GDT/LDT在内存中的位置。80x86系列中引入了两个新寄存器GDRLDR,其中GDR用于表示GDT在内存中的段地址和段限(就是表的大小),因此GDR是一个48位的寄存器,其中32位表示段地址,16位表示段限(最大64K,每个描述符8字节,故最多有64k/8=8k个描述符)。LDR用于在内存中的位置,但是因为LDT本身也是一种数据段,它必须有一个描述符,且该描述符必须放在GDT中,因此LDR使用了DSESCS等相同的机制,其中只存放一个“选择子”,通过查GDT表获得LDT的真正内存地址。

    对了,还有中断要考虑,在80x86系列中为中断服务提供中断/陷阱描述符,这些描述符构成中断描述符(IDT),并引入一个48位的全地址寄存器存放IDT的内存地址,理论上IDT表同样可以有8K项,可是因为80x86只支持256个中断,因此IDT实际上最大只能有256项(2k大小)。

第五:新要求---任务篇

        前面介绍了保护模式的基本问题,也是核心问题,解决了上面的问题,程序就可以在保护模式下运行了。

        但众所周知80286以后在保护模式下实现了对多任务的硬件支持。我的第一反应是:为什么不在实模式下支持多任务,是不能还是不愿?

        思考之后,我的答案是:实模式下能实现多任务。因为多任务的关键是有了描述符,可以给出关于数据段的额外描述,如权限等,进而在这些附加信息的基础上进行相应的控制,而实模式下缺乏描述符,但假设我们规定各段的前2个字节或若干字节用于描述段的附加属性,我觉得和使用描述符这样的机制没有本质区别,如果再附加其他机制

        基于上述,我更倾向于任务任务是独立于保护模式之外的功能。下面我们来分析一下任务,任务的实质是什么呢?很简单,就是程序。所谓任务切换其实就是程序的切换!!

        现在问题明朗了。实模式下程序一个接一个的运行,因此程序运行的环境不必保存;保护模式下可能一个程序在运行过程中被暂停,转而执行下一个程序,我们要做什么?很容易想到保存程序运行的环境就行了(想想游戏程序中的保存进度功能),不如各寄存器值等。

        显然这些环境数据构成了一类新的数据段(即TSS)。沿用前面的思路,这些类段数据设置描述符(TSS描述符),将该类描述符放在GDT中(不能放在LDT中,因为80x86不允许),最后再加一个TR寄存器用于查表。TR是一个起“选择子”作用的寄存器,16位。

        好了,任务切换的基本工作就是将原任务“环境”存入TSS数据段,更新TR寄存器,系统将自动查GDT表获得并载入新任务的“环境”,然后转到新任务执行。

 

第六:附加要求---分页篇

        为什么叫附加要求,因为现在任务还不能很好地工作。前面说过,任务实质上是程序,不同的程序是由不同的用户写的,所有这些程序完全可能 使用相同的地址空间,而任务的切换过程一般不会包括内存数据的刷新, 不是不可能,而是如果那样做太浪费了。因此必须引入分页机制才可能有效 地完成对多任务的支持。

        分页引入的主要目标就是解决不同任务相互之间发生地址冲突的问题。 分页的实质就是实现程序内地址到物理地址的映射,这也是一个“地址转换” 机制,同样可以使用前面的方案(即类似GDT的做法):首先建立页表这样一种数据段,80x86中使用二级页表方案,增设一个CR3寄存器用于存放 一级页表(又称为页目录)在内存中的地址,CR332位,其低12位总是为 零,高20位指示页目录的内存地址,因此页目录总是按页对齐的。CR3作为 任务“环境”的一部分在任务切换时被存入TSS数据段中。 当然还得有相应的缺页中断机制及其相关寄存器CR2(页故障线性地址寄存器)。

 

第七:总结

         保护模式下增加了什么?

1、寄存器 GDR LDR IDR TR CR3

2、数据段 描述符表(GDT LDT) 任务数据段(TSS) 页表(页目录 二级页表)

3、机制权限检测(利用选择子/描述符/页表项的属性位)

              线性地址到物理地址的映射

 

第八: 保护模式常用的名词解释

前面内容中出现过的不再解释。

 1RPL 选择子当中的权限位确定的权限

2CPL 特指CS中的选择子当中的权限位确定的权限

3EPL EPL=Max(RPL,CPL),RPLCPL中数值较大的,或说权限等级较小的

4DPL 描述符中的权限位确定的权限

5PL 泛指以上4种特权级

6、任务特权 =CPL

7I/O特权 EFLAGS寄存器的位1314确定的权限

8、一致代码段 一种特殊的代码段,它在CPL>=DPL时允许访问

                正常的代码段在CPL=DPL RPL<=DPL时才允许访问

 

第九:x86的控制寄存器

CR0,CR1,CR2,CR3

状态和控制寄存器组除了EFLAGSEIP ,还有四个32位的控制寄存器,它们是CR0CR1CR2CR3

这几个寄存器中保存全局性和任务无关的机器状态。

CR0
中包含了6个预定义标志,0位是保护允许位PE(Protedted Enable),用于启动保护模式,如果PE位置1,则保护模式启动,如果PE=0,则在实模式下运行。1位是监控协处理位MP(Moniter coprocessor),它与第3位一起决定:当TS=1时操作码WAIT是否产生一个协处理器不能使用的出错信号。第3位是任务转换位(Task Switch),当一个任务转换完成之后,自动将它置1。随着TS=1,就不能使用协处理器。CR0的第2位是模拟协处理器位 EM (Emulate coprocessor),如果EM=1,则不能使用协处理器,如果EM=0,则允许使用协处理器。第4位是微处理器的扩展类型位 ET(Processor Extension Type),其内保存着处理器扩展类型的信息,如果ET=0,则标识系统使用的是287协处理器,如果 ET=1,则表示系统使用的是387浮点协处理器。CR0的第31位是分页允许位(Paging Enable),它表示芯片上的分页部件是否允许工作。

CR1
是未定义的控制寄存器,供将来的处理器使用。

CR2
是页故障线性地址寄存器,保存最后一次出现页故障的全32位线性地址。

CR3
是页目录基址寄存器,保存页目录表的物理地址,页目录表总是放在以4K字节为单位的存储器边界上,因此,它的地址的低12位总为0,不起作用,即使写上内容,也不会被理会。

     
这几个寄存器是与分页机制密切相关的,因此,在进程管理及虚拟内存管理中会涉及到这几个寄存器,读者要记住CR0CR2CR3这三个寄存器的内.

阅读(814) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~