您的位置:首页 > 其它

学习笔记--保护模式理论初步(一)

2016-09-03 10:01 211 查看
一.保护方式简介
                       

                                                                                     注:本文部分节选自《80X86汇编语言程序设计教程  杨季文》

80386有三种工作方式:实模式,保护模式和虚拟8086模式。本文介绍保护方式下的80386及相关的程序设计内容。实模式下的80386寄存器,寻址方式和指令等基本概念,除特别说明外在保护方式下仍然保持。

尽管实方式下 80386的功能要大大超过其先前的处理器(8086/8088,80186,80286),但只有在保护方式下,80386才能真正发挥更大的作用。在保护方式下,全部32条地址线有效,可寻址高达4G字节的物理地址空间;扩充的存储器分段管理机制和可选的存储器分页管理机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持;支持多任务,能够快速地进行任务切换和保护任务环境;4个特权级和完善的特权检查机制,既能实现资源共享又能保证代码和数据的安全和保密及任务的隔离;支持虚拟8086方式,便于执行8086程序。 

<一> 存储管理机制 

为了对存储器中的程序及数据实现保护和共享提供硬件支持,为了对实现虚拟存储器提供硬件支持,在保护方式下,80386不仅采用

扩充的存储器分段管理机制,而且提供可选的存储器分页管理机制。这些存储管理机制由80386存储管理部件MMU实现。

1.目标

80386 有 32根地址线,在保护方式下,它们都能发挥作用,所以可寻址的物理地址空间高达4G字节。在以80386及其以上处理器为CPU的PC兼容机系统中,把地址在1M以下的内存称为常规内存,把地址在1M以上的内存称为扩展内存。

80386 还要对实现虚拟存储器提供支持。虽然与 8086 可寻址的1M字节物理地址空间相比,80386可寻址的物理地址空间可谓很大,但实际的微机系统不可能安装如此达的物理内存。所以,为了运行大型程序和真正实现多任务,必须采用虚拟存储器。虚拟存储器是一种软硬件结合的技术,用于提供比在计算机系统中实际可以使用的物理主存储器大得多的存储空间。这样,程序员在编写程序时不用考虑计算机中物理存储器的实际容量。

80386 还要对存放在存储器中的代码及数据的共享和保护提供支持。任务甲和任务乙并存,任务甲和任务乙必须隔离,以免相互影响。但它们又可能要共享部分代码和数据。所以,80386既要支持任务隔离,又要支持可共享代码和数据的共享,还要支持特权保护。

2.地址空间和地址转换

保护方式下的虚拟存储器由大小可变的存储块构成,这样的存储块称为段。80386采用称为描述符的数据来描述段的位置、大小和使用情况。虚拟存储器的地址(逻辑地址)由指示描述符的选择子和段内偏移两部分构成,这样的地址集合称为虚拟地址空间。80386支持的虚拟地址空间可达64T字节。程序员编写程序时使用的存储地址空间是虚拟地址空间,所以,他们可认为有足够大的存储空间可供使用。

显然,只有在物理存储器中的程序才能运行,只有在物理存储器中的数据才能访问。因此,虚拟地址空间必须映射到物理地址空间,二维的虚拟地址必须转化成一维的物理地址。由于物理地址空间远小于虚拟地址空间,所以只有虚拟地址空间中的部分可以映射到物理地址空间。由于物理存储器的大小要远小于物理地址空间,所以只有上述部分中的部分才能真正映射到物理存储器。 每一个任务有一个虚拟地址空间,为了避免多个并行任务的多个虚拟地址空间直接映射到同一个物理地址空间,采用线性地址空间隔离虚拟地址空间和物理地址空间。线性地址空间由一维的线性地址构成,线性地址空间和物理地址空间对等。线性地址32位长,线性地址空间容量为4G字节。

80386 分两步实现虚拟地址空间到物理地址空间到物理地址空间的映射,也就是分两步实现虚拟地址到物理地址的转换,但第二步是可选的。下图是地址映射转换的示意图 



通过描述符表和描述符,分段管理机制实现虚拟地址空间到线性地址空间的映射,实现把二维的虚拟地址转换为一维的线性地址。
这一步总是存在的。

分页管理机制把线性地址空间和物理地址空间分别划分为大小相同的块,这样的块称为页。通过在线性地址空间的页与物理地址空
间的页建立之间建立的映射表,分页管理机制实现线性地址空间到物理地址空间的映射,实现线性地址到物理地址的转换。分页管理机
制是可选的,在不采用分页管理机制时,线性地址空间就等同于物理地址空间,线性地址就等于物理地址。

分段管理机制所使用的可变大小的块,时分段管理机制比较适宜处理复杂系统的逻辑分段。存储块的大小可以根据适当的逻辑含义
进行定义,而不用考虑固定大小的页所强加的人为限制。每个段可作为独立的单位处理,以简化段的保护及共享。分页机制使用的固定
大小的块最适合于管理物理存储器,无论是管理内存还是外存都同样有效。分页管理机制能够有效地支持实现虚拟存储器。

段及分页这两种机制是两种不同的转换机制,是整个地址转换函数的不同的转换级。虽然两种机制都利用存储在主存储器中的转换
表,但这些表具有独立的结构。事实上,段表存储在线性地址空间,而页表存储在物理地址空间。因此,段转换表可由分页机制重新进
行定位而不需段机制的参与。段转换机制把虚拟地址转换为线性地址,并在线性地址中访问段转换机制的表格,而不会觉察分页机制已
把线性地址转换为物理地址。类似地,分页机制对于程序产生的地址所使用的虚拟地址空间一无所知。分页机制只是直接地把线性地址
转换为物理地址,并且在物理地址中访问转换表格,并不知道虚拟地址空间的存在,甚至不知道段转换机制的存在。

3.虚拟存储器概念

虚拟存储器是一种设计技术,用于提供比在计算机系统中实际可以使用的物理主存储器大得多的存储空间。使用者会产生一种错觉,好象在程序中可以使用非常大的物理存储空间。使用虚拟存储器的好处是:一个程序可以很容易地在物理存储器容量大不一样的、配置范围很广的计算机上运行;编程人员使用虚拟存储器可以写出比任何实际配置的物理存储器都大得多的程序。虚拟存储器由存储管理机制及一个大容量的快速硬盘存储器支持。在程序运行的任何时刻,只把虚拟地址空间的一小部分映射到主存储器,其余部分则存储在磁盘上。因为只有存储在主存储器中的部分虚拟存储器可由处理器使用,这种虚拟存储技术将依赖程序内部访问存储器的局部化特性,在程序执行中只需整个虚拟存储器中的少量存储内容在主存储器中驻留。而当访问存储器的范围发生变化时,有必要把虚拟存储器的某些部分从磁盘调入主存储器,虚拟存储器的另外的部分,也能从主存储器传送回磁盘上。地址转换机制以两种方式支持虚拟存储器。

第一,把实际驻留在主存储器中的那部分虚拟存储器标记为无效,并建立起虚拟存储器驻留部分的虚拟--物理映射关系,把驻留部分的相应虚拟存储器地址,转换为对应物理存储器的地址。如果程序访问的虚拟地址对应于虚拟存储器未驻留的部分,将由于无效映射信息而引起异常。操作系统通过把未驻留部分从磁盘上读入到主存储器中,来处理这种异常,并根据需要更新地址转换表。在引起异常 的原因排除以后,异常处理程序完成异常事件的处理,并返回原来的程序恢复执行。在后面的文章中将会看到,从异常处理程序返回后,这时要重新执行一次原来引起异常的指令,而该指令在后一次执行时自然会成功地完成。

第二,地址转换机制通过收集驻留在主存储器中的虚拟存储器部分的使用统计信息来支持虚拟存储器,这些使用统计信息,在主存
储器空间紧缺时,帮助操作系统决定可以将哪些部分传送回磁盘。


<二>保护机制
为了支持多任务,对各任务实施保护是必需的。从80286开始,处理器就具备了保护机制。保护机制能有效地实现不同任务之间的

保护和同一任务内的保护。

1.不同任务之间的保护

保护的一个重要方面是应用程序之间的保护。通过把每个任务放置在不同的虚拟地址空间的方法来实现任务与任务的隔离,达到应用程序之间保护的目的。虚拟地址到物理地址的映射函数在每个任务中进行定义,随着任务切换,映射函数也切换。任务A的虚拟地址空间映射到物理地址空间的某个区域,而任务B的虚拟地址空间映射到物理地址空间的另外区域,彼此独立,互不相干。因此,两个不同的任务,尽管虚拟存储单元地址相同,但实际的物理存储单元地址可以不同。

每个任务各有一组独立的映射表,即具有不同的地址转换函数。在80386上,每个任务都有自己的段表及页表。当处理器进行切换并执行新的任务时,这种任务切换的一个重要部分,就是为新任务切换任务的转换表。为了使操作系统与所有的应用程序相隔离,可以把操作系统存储在一个单一的任务中。然而,我们即将看到,在一个任务内操作的保护机制,更适合于保护操作系统,使其不被应用程序破坏。这种机制,使操作系统由所有任务共享,并且可在每一任务中对其进行访问,而且仍然保护了操作系统,使其不被应用程序破坏。这种保护操作系统的方法,是把操作系统存储在虚拟地址空间的一个公共区域,然后,再使每一任务按此区域分配一个同样的虚拟地址空间,并进行同样的虚拟--物理地址映射。各个任务公用的这部分虚拟地址空间,被称为全局地址空间。

仅由一个任务占有的虚拟地址空间部分,即不被任何其它任务共享的虚拟地址部分,称为局部地址空间。局部地址空间包含的代码
和数据,是任务私有的,需要与系统中的其它任务相隔离。

再每个任务中有不同的局部地址空间。因此,两个不同的任务中,对同一虚拟地址的访问,实际上转换为不同的物理地址。这就使
操作系统对每个任务的存储器,可以赋予相同的虚拟地址,仍然保证任务的隔离。另一方面,对全局地址空间中同一虚拟地址的访问,
在所有任务中都转换为同样的物理地址,从而支持公共的代码及数据的共享,例如对操作系统的共享。

2.同一任务内的保护

在一个任务之内,定义有四种执行特权级别,用于限制对任务中的段进行访问。按照包含在段中的数据的重要性和代码的可信程度,给段指定特权级别。把最高的特权级别分配给最重要的数据段和最可信任的代码段。具有最高特权级别的数据,只能由最可信任的代码访问。给不重要的数据段和一般代码段分配较低的特权级别。具有最低特权级别的数据,可被具有任何特权级别的代码访问。

特权级别用数字 0、1、2和3表示,数字0表示最高特权级别,而数字3 表示最低特权级别,即数字较大的级别具有较低的特权。 

四种特权级的层次关系如下图(图中右边的数字为特权级)所示 



每一特权级都有各自独立的程序堆栈,以避免与共享栈区有关的保护问题。当一个程序从一个特权级切换到另一个特权级执行时,程序使用的堆栈,从原特权级的栈段改变为新特权级的栈段。对于堆栈段寄存器SS来说,描述符特权级(DPL)必须等于当前代码段的特权级(CPL)。从一个特权级切换到另一特权级的方法将在控制转移方法一文中描述。

每个存储器段都与一个特权级别相联系。特权级别限制是指,只有足够级别的程序,才可对相应的段进行访问。在任何时候,一个任务总是在四个特权级之一下运行,任务在特定时刻的特权级称为当前特权级(Current Privilege level),标记为CPL,即当前运行程序的特权级。每当一个程序试图访问一个段时,就把CPL与要访问的段的特权级进行比较,以决定是否允许这一访问。对给定CPL执行的程序,允许访问同一级别或外层级别的数据段。如上图所示,CodeK可访问同级的数据段DataK,也可访问外层的DataOS、DataAP1及DataAP2等。如果试图访问内层级别的数据段则是非法的,并引起异常。如上图所示,CodeOS可访问同级的DataOS,也可访问外层的DataAP1和DataAP2等,但不能访问内层的DataK。

虽然应用程序都在最外层,但由于各个不同的应用程序存储在不同的虚拟地址空间中,所以各应用程序被隔离保护。如上图所示,最外层的CodeAP1只能访问DataAP1,不可能访问同级的另一应用程序的DataAP2;同样,CodeAP2只能访问DataAP2,不可能访问DataAP1。

这实际上是组合保护。应用程序 1 和操作系统构成任务A,应用程序2和操作系统构成任务 B。操作系统被任务A和任务 B共享,在任务A和任务B的两个不同的虚拟地址空间中,操作系统占用虚拟地址空间相同的部分。

特权级的典型用法是,把操作系统的核心部分放在0级,操作系统的其余部分放在 1级,而应用程序放在3 级,留下的2级供中间软件使用。对特权级进行这样的安排,使得在0级的操作系统核心有权访问任务中的所有存储段;而在3级的应用程序只能访问程序本身的存储段,这些存储段也是在3级(注意,Windows 9X 操作系统只使用了 0
级和3级,以便于移植到精简指令集的计算机上,如RS4000等,这些处理器一般只有两个特权级,即系统级和用户级)。 

二、分页管理机制

后面讲解,这里暂时不需要了解那么多。

三、分段管理机制 

 插播->有了分段机制,实模式与保护模式下的寻址也有所不同:

在实模式下,也就是在8086系统下的寻址方式。
Intel 8086是16位的CPU,它有着16位的寄存器(Register),16位的数据总线(Data Bus)以及20位的地址总线(Address Bus)和1MB的寻址能力。一个地址是由段和偏移两部分组成的,物理地址遵循这样的计算公式:
物理地址(Physical Address) = 段值(Segment) * 16 + 偏移(Offset)。其中段值和偏移都是16位的。故寻址范围为1MB。

在保护模式下,首先使用段选择子在段描述符表中查找到相对应的段描述符,找到32位段基址,然后在与32位的偏移量相加,得到线性地址。段基址和段偏移量都是32位的,所以寻址范围大小为4GB



目前是不是有点看不懂,没关系,暂时看不懂就放在这里吧,但是要留个印象,后面会慢慢解释的。

<一>段定义和虚拟地址到线性地址的转换

段是实现虚拟地址到线性地址转换机制的基础。在保护方式下,每个段由如下三个参数进行定义:段基地址(Base Address)、段界限(Limit)和段属性(Attributes)。

段基地址规定线性地址空间中段的开始地址。在80386保护方式下,段基地址长 32位。因为基地址长度与寻址地址的长度相同,所以任何一个段都可以从32位线性地址空间中的任何一个字节开始,而不象实方式下规定的边界必须被16整除。

段界限规定段的大小。在 80386保护模式下,段界限用20 位表示,而且段界限可以是以字节为单位或以4K字节为单位。段属性中有一位对此进行定义,把该位成为粒度位,用符号G标记。G=0表示段界限以字节位位单位,于是20
位的界限可表示的范围是1字节至 1M字节,增量为1字节;G=1表示段界限以4K字节为单位,于是20位的界限可表示的范围是4K 字节至4G字节,增量为 4K字节。当段界限以4K字节为单位时,实际的段界限LIMIT 可通过下面的公式从 20位段界限Limit计算出来:

LIMIT=limit*4K+0FFFH=(Limit SHL 12)+0FFFH

所以当粒度为 1时,段的界限实际上就扩展成32 位。由此可见,在80386保护模式下,段的长度可大大超过 64K字节。基地址和界限定义了段所映射的线性地址的范围。基地址Base是线性地址对应于段内偏移为 0的虚拟地址,段内偏移为X 的虚拟

地址对应 Base+X的线性地址。段内从偏移0 到Limit范围内的虚拟地址对应于从 Base到Base+Limit范围内的线性地址。下图表示一个段如何从虚拟地址空间定位到线性地址空间。图中BaseA等代表段基地址, LimitA等代表段界限。另外,段C 接在

段 A之后,也即BaseC=BaseA+LimitA。 



例如:设段 A的基地址等于00012345H,段界限等于5678H,并且段界限以字节为单位(G=0),那么段A对应线性地址空间中从00012345H-000179BDH的区域。如果段界限以4K 字节为单位(G=1),那么段A对应线性地址空间中从00012345H-0568B344H(=00012345H+5678000H+0FFFH)的区域。

通过增加段界限,可以使段的容量得到扩展。这对于那些要在内存中扩展容量的普通数据段很有效,但对堆栈段情况就不是这样。因为堆栈底在高地址端,随着压栈操作的进行,堆栈向低地址方向扩展。为了适应普通数据段和堆栈数据段在两个相反方向上的扩展,数据段的段属性中安排了一个扩展方向位,标记为ED。ED=0表示向高端扩展,ED=1表示向低端扩展。一般只有堆栈数据段才使用向低端扩展的属性(堆栈段也可使用向上扩展的段),这是因为,向下扩展的段是为以下两个目的而设计的:

第一,堆栈段被定义为独特段,即 DS 和SS包含不同的选择器。

第二,一个堆栈段是靠将它复制到一个更大的段来扩充自己(而不是靠将现存的页增加到它的段上)。不打算用这种方法实现堆栈的设计者不需要定义向下扩展的段。

需要注意的是,只有数据段的段属性中才有扩展方向属性位ED,也就是说只有数据段(堆栈段作为特殊的数据段)才有向上扩展和向下扩展之分,其它段都是自然的向上扩展。

数据段的扩展方向和段界限一起决定了数据段内偏移的有效范围。当段最大为1M字节时,在向高端扩展的段内,从 0到Limit的偏移是合法有效的偏移,而从Limit+1到 1M-1的偏移是非法无效的偏移;在向低端扩展的段内,情形刚好相反,从0到 Limit的偏移是非法无效的偏移,而从Limit+1到 1M-1的偏移是合法有效的偏移,注意边界值Limit对应地址的有效性。段最大为 4G时,情形类似。由此可见,如果一个段是向下扩展的,则所有的偏移必须大于限长,因为其限长是指下限,其基地址从高地址出开始。反之,若一个段是向上扩展的,则所有偏移必须小于等于限长,因为其限长是指上限,基地址从低地址处开始。通过使用段环绕,可以把向下扩展段定义到任何线性地址且可定义为任何大小。

在每次把虚拟地址转换为线性地址的过程中,要对偏移进行检查。如果偏移不在有效的范围内,那么就引起异常。

段属性规定段的主要特性。例如上面已经提到的段粒度G就是段属性的一部分。在对段进行各种访问时,将对访问是否合法进行检查,主要依据是段属性。例如:如果向一个只读段进行写入操作,那么不仅不能写入,而且会引起异常。在下面会详细说明各个段属性位的定义和作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐