精华内容
下载资源
问答
  • 保护模式

    2012-12-25 23:41:28
    386之后,引入了处理器的保护模式,在计算机发展中,这是一个大事件,因为保护模式概念的提出,使现代操作系统成为可能。如果不采用虚拟地址的概念,那么现代操作系统要管理如此庞大的资源,肯定是一个噩梦。  ...

     386之后,引入了处理器的保护模式,在计算机发展中,这是一个大事件,因为保护模式概念的提出,使现代操作系统成为可能。如果不采用虚拟地址的概念,那么现代操作系统要管理如此庞大的资源,肯定是一个噩梦。

        实模式:是指直接访问物理内存,指令中的地址值是物理内存地址。

        保护模式:是指指令中的地址不再是物理地址,而是虚拟地址,要经过MMU的处理转换成物理地址。

        由于保护模式的提出正在32位系统发展之初,因此,实模式一般为16位系统,而保护模式变成了32位地址,这不是实模式与保护模式的本质区别,只是时间的巧合。

        在多任务系统中,其目的就是保护各个任务能独立运行,而不被其他任务影响。386的CPU是怎样处理的呢?

        现在假设有A和B两个程序要运行(程序运行后一般就成了操作系统中的一个进程,或者叫一个任务,程序、进程、任务可以理解成一个概念),首先弄清楚程序是什么。

       程序 是一段代码和数据组成的文件,其目的是为了完成某个任务,如编辑文件、播放音乐。这段代码是可以独立在CPU中运行的,但是这段代码与操作系统是什么关系呢?

       1. 操作系统是程序的管理者, 如果没有操作系统,程序是一个存放在硬盘上的文件,不会自动调入到内存中运行,操作系统可以为程序创建一个合适的运行环境。

       2. 操作系统为程序提供一些最基本的功能,如读写磁盘、管理内存分配。而这些功能是以API方式提供给程序调用的,也就是说在程序中会调用大量的操作系统提供的子程序(API),如果没有这些子程序,你向屏幕print一个字符,画一个图形窗口,就需要自己写出这些功能,那编写程序就是个噩梦。

       从CPU角度看程序:程序就是小段代码,只是整个大程序的一小部分, 程序的绝大部分是操作系统提供的代码,而程序员只是编写了调用API的小部分代码来完成自己需要的功能。

      从内存角度看程序:程序只是一片数据,这片数据中包含了代码和处理的数据,至于代码从哪开始执行,程序之间怎么跳转,由程序代码本身决定。

      从操作系统角度看程序:程序仅仅是被本操作系统管理的一段代码和数据,我可以决定把这段代码放到内存的哪个地方,也可以决定什么时候启动这段代码的运行。

      第一步,BIOS启动:系统上电后,CPU开始执行第一条程序,CPU永远也不知道哪个是操作系统内核,哪个是用户程序,它唯一会做的就是执行一条指令,并输出结果,然后再从内存的紧接着的单元取下一条指令。而程序的跳转是由指令本身修改指令指针(PC)指向新的内存地址(或者由外部中断或异常引起PC的修改)实现的。CPU上电后,PC从地址0开始执行(或者从其他地址开始装载指令,这取决于CPU的设计),而地址0处必然是一条可执行指令,在个人计算机中,CPU上电是从主板的BIOS中开始执行的,为什么不从其他地方执行呢,这是由硬件设计规则决定的,BIOS是挂接在CPU的地址总线上的。BIOS程序会完成主板的监测和基本初始化,并构建起中断向量表,然后再从硬盘(也可以是其他存储设备)加载第一个扇区内容到内存,从哪里加载引导扇区,可以在CMOS中设定,而对于不同存储设备的读写必须有BIOS的支持,如果BIOS中没有USB设备的读写程序(驱动),那么CMOS中也不会让用户可以选择从USB启动系统。

        当BIOS从磁盘加载第一个引导扇区(512字节),之后,会检查其中是否可执行代码,如果不是,那么就报错“不是启动盘”。

       第二步,操作系统引导:磁盘的第一个扇区中会有信息指出哪个分区是活动分区,加载活动分区的引导扇区,正式启动操作系统的加载过程。此时是运行于实模式的,因为此时的CPU处于初始化前的状态,寄存器中的值是初始化值,只有更改寄存器的值为某些特定值才能启动保护模式。操作系统的引导程序会初始化基本的硬件系统,如内存控制器、堆栈及其他一些CPU寄存器。然后会把操作系统内核拷贝到内存中,内核在内存中的物理位置有些是固定的,有些是不需要固定的,对于哪些是必须固定在特定地址,这由操作系统设计决定。

      第三步,启动保护模式,在进入保护模式前,我们需要做什么准备呢? 在386的保护模式中,GDTR是必须要初始化的,这个寄存器保存了全局描述符表的虚拟地址,注意是虚拟地址,一旦启动保护模式,所有的地址都是虚拟地址,而不是物理地址。什么是全局描述符,这个是由操作系统设计者决定,还是CPU设计者决定呢?这个是CPU设计者决定的,因为GDTR中的描述符格式对于每个操作系统来说都一样,描述符大小为8个字节,有多个个描述符由GDTR的值中的一个域(某部分位)决定。关于GDTR格式可以参考相关文件,而描述符用来做什么呢? 描述符就是用来描述整个内存中放了什么东西,每一项描述了该段内存的起始地址、性质(如是代码还是数据)、长度等。而在其他一些寄存器中,就不再对内存做具体描述,如代码段寄存器CS,其值(16位值)仅仅包含一个索引值(如08表示在GDT表中的第8项)。

       还有一个寄存器CR3,是页表基址寄存器,是告诉MMU,页表存放在内存中哪个位置了,GDT表和页表都是操作系统引导程序必须首先建立的内存数据,其位置可以在任何地方,其开始地址会被引导程序放入到对应的寄存器GDTR和CR3中。页表是什么呢?首先弄明白为什么要引入页表,在多任务环境中,我们要运行不同的程序,为了不引起程序之间的相互影响,引入了“保护模式”的概念。

       给不同的程序分配不同的内存块,这个块可大,可小。由操作系统来管理,例如程序A访问内存空间0x00001000~0x00002000,程序B访问0x00003000~0x00004000,似乎是合理的,但是我们知道,从CPU的角度来看,当一个指令执行时,可以访问任何的4G空间(32位系统),CPU指令设计者并没有规定什么指令只能访问某个空间,操作系统内核以及程序A和程序B,所有的指令代码是同一套。那么怎样才能限制程序A不至于访问到程序B的空间。首先我们看现代操作系统的“分时”概念,分时系统中,是设定一个计时器中断,如每隔10ms就产生一个中断,这个中断发生会时就跳转到中断处理程序,而这个程序就是操作系统内核进程调度的核心。因此任何任务的运行最多一次连续运行10ms,之后就产生中断接管CPU,操作系统根据自己设计的规则(这个规则相当复杂的,也是一个优秀操作系统性能的体现)决定再次启动那个进程运行。当我们切换任务(进程)的时候,就必须先保存当前正在运行的进程(如寄存器值),然后恢复某个进程的状态(如装入寄存器值)。

     假设我们有某种映射机制,让0x00000000与物理地址0x00001000对应,当程序A中一个指令 MOV EAX, [0x00000000]执行时,实际访问的是0x00001000,而当程序B运行同样的指令,访问的是0x00003000,那么至少要存在两个条件:

          1)可以在指令执行前将指令译码,并能及时修改其中的虚拟地址为对应的物理地址,这个在具备保护模式的CPU中都有一个叫MMU的硬件来处理,因为这个处理越快越好,因此必须采用硬件处理,而且必须计算简单。

         2)有了处理部件,还需要有“米”,也就是需要有映射表,到底哪个虚拟地址对应哪个物理地址呢?这个就是页表的功能,我们在内存中如果直接画出一块内存给程序A使用,例如从0x00001000开始的100MB空间给程序A,但是程序A可能只需要10MB的空间,因此不能给每个进程首先分配固定大小的内存区域。因为随着程序的运行,内存消耗不断变化,因此就必须动态给你分配内存。

        先不管页表具体怎么构建,首先应该明白:

       1)每个进程都有自己的页表,用来把虚拟地址映射到物理地址,这样相同的虚拟地址就可以映射到不同的物理地址。

       2)当每次进程切换时,CR3(ARM处理器为协处理器的寄存器C2保存页表基址)的值也会不同,用于指向进程自己的虚拟地址映射页表。

      386保护模式中,有分段模式和分页模式:

        分段模式,是用来直接把不同的进程的虚拟地址映射到一整块内存中,如进程A的内存被映射到0x00100000开始的10MB空间,这种模式,是映射简单,但是必须清楚每个程序需要用到的内存大小,不适合于做大型操作系统。因此windows和linux均采用分页模式,也不使用分段,也就是说段寄存器是没用的。

       分页模式中,   存储器按照4K(或4K的倍数)来划分成颗粒,每个小块可以分配给不同的进程使用。

        如一个虚拟地址0x00001000,被MMU怎么处理成物理地址呢?

        在分段模式下,虚拟地址是一个段内偏移地址,而物理地址= 段寄存器值 + 偏移地址(程序指令中的地址)。每个进程的段寄存器是不同的,段寄存器的值是由操作系统管理的,保证了不同进程间的物理内存地址不会重叠。

       在分页模式下,MMU会自动从CR3寄存器找到页表基址,然后根据虚拟地址(程序指令中的地址)的前20位找到页表中该页(每个虚拟地址对应的页号是确定的)所对应的物理页(物理页大小4K),而虚拟地址的低12位是该页内的偏移地址。页表分为页表目录和具体的页表,数据量是比较大的,MMU之所以能根据虚拟地址快速计算出物理内存,完全取决于虚拟地址的格式是固定的,正由于虚拟地址到物理地址的映射必须要查表,因此速度必然会收到影响。 具体的页表的处理可以查阅相关文档。

       在386中,一个段描述符,包含有权限控制,如是否可执行,是在ring0可执行,还是ring3可执行。 在windows中,不使用分段方式管理内存,但是仍然使用段寄存器的权限控制,如在windows下,cs段寄存器永远指向GDT表的第8项,而GDT中的段描述符中规定了当前用户程序进程的执行权限是ring3,当切换到内核模式下时,cs则指向GDT的第一项,指明了内核当前状态可执行的权限级别为ring0,这样,当CS不改变时,用户程序是不能执行只能在ring0级别才能执行的指令(如IO操作)。

      (在保护模式与实模式下,其地址运算模式其实是不变的,这是因为使用了不可访问的32位段寄存器,这个寄存器是CPU自己维护的。如CS寄存器被程序更改时,CPU会自动根据当前的模式改变32位影子段寄存器,如果是实模式下,直接复制16位的cs寄存器值到32位寄存器中,如果是保护模式,则是从GDT表中加载相应项的段基址到32位CS寄存器中。对于地址的计算,CPU永远是采用不可访问的32位隐藏寄存器进行计算,物理地址 = 段基址 + 虚拟偏移地址。 在windows中,由于所有的段基址=0,所以实际上物理地址就不用考虑段基址。可以想象,如果从实模式切换到保护模式,如果不修改cs的值,那么其段基址仍然有效,这样指令中的地址实际上就是偏移地址。操作系统引导程序也可以利用这个原理,当切换到保护模式时,仍然可以按照实模式下的物理地址访问方式访问内存,保证切换到保护模式时不至于引起程序指针PC的值变成无效地址。)

        采用分页管理内存后,每个进程占用了物理内存的一些内存页,这些页可以是连续的,也可以是不连续的,这些页被页表索引,页表中的虚拟地址是连续的,称为“线性地址”,例如页表中第100页的物理基址为0x000A0000,而第101页的物理基址可能为0x01000000,但是虚拟地址100页和101页是连续的。

       进程就不必关心物理地址的分配,因为每个进行的虚拟地址都是独立的,程序就可以访问4G虚拟空间,而4G内的任何一个地址会被页表转化成实际的物理地址。这样编译器和程序员就不在关心其他程序的存在,而仅仅关注自己的4G虚拟地址空间。当然因为程序要使用大量的内核API,而这些代码是被放在高2G或高1G空间的,因此用户程序不能直接访问这些内核空间。这种限制是通过操作系统软件设置决定的。

        由于进程切换是强制性的,到时间(如10ms)就切换,因此内核程序为不同的进程服务(被不同的进程代码调用),因此其访问安全性的设计非常重要,内核不能直接访问用户空间的地址(如0~2G),并不是指令不容许,而是这样的访问不安全。

    展开全文
  • 实模式和保护模式

    2020-05-29 12:02:42
    实模式和保护模式实模式保护模式 实模式 实模式是什么? 保护模式

    实模式

    实模式是什么?实模式是指8086CPU的寻址方式,寄存器大小,指令用法等,是用来反应CPU在该环境下如何工作的概念。

    先讲一下CPU 的工作原理:
    1,控制单元要取下一条待运行的指令,该指令的地址在程序计数器 PC 中,在 x86CPU 上,程序计数器就是 cs: ip。于是读取 ip 寄存器后,将此地址送上地址总线, CPU 根据此地址便得到了指令,并将其存入到指令寄存器 IR 中。
    2,这时候轮到指令译码器上场了,它根据指令格式检查指令寄存器中的指令,先确定操作码是什么,再检查操作数类型,若是在内存中,就将相应操作数从内存中取回放入自己的存储单元,若操作数是在寄存器中就直接用了,免了取操作数这一过程。
    3,操作码有了,操作数也齐了,操作控制器给运算单元下令,开工,于是运算单元便真正开始执行指令了。
    4,ip 寄存器的值被加上当前指令的大小,于是 ip 又指向了下一条指令的地址。
    –》 接着控制单元又要取下一条指令了,流程回到了本段开头。
    CPU 便开始了日复一日的循环,由于 CPU 特别不容易坏,所以唯一它停下来的条件就是停电。

    通过上面的说明,我们知道CPU执行程序是要知道程序指令的地址的,就像你要去吃饭,你得知道餐厅在哪里一样。CPU通过cs:ip的组合在内存里寻址,只要将转换后的地址提交给地址总线,就能找到该数据,指哪打哪。
    参考:平坦模型与分段模型

    实模式有很多不靠谱的地方,实模式被保护模式淘汰的原因,最主要是安全隐患。
    (1)实模式下操作系统和用户程序属于同一特权级,这哥俩平起平坐,没有区别对待。
    (2)用户程序所引用的地址都是指向真实的物理地址,也就是说逻辑地址等于物理地址,实实在在地
    指哪打哪。
    (3)用户程序可以自由修改段基址,可以不亦乐乎地访问所有内存,没人拦得住。
    以上 3 个原因属于安全缺陷,没有安全可言的 CPU 注定是不可依赖的,这从基因上决定了用户程序乃至操作系统的数据都可以被随意地删改,一旦出事往往都是灾难性的,而且不容易排查。
    (4)访问超过 64KB 的内存区域时要切换段基址,转来转去容易晕乎。
    (5)一次只能运行一个程序,无法充分利用计算机资源。
    (6)共 20 条地址线,最大可用内存为 1MB,这即使在 20 年前也不够用

    保护模式

    保护模式是相对于实模式而言的,它首次出现是在Intel 80286 CPU中首次出现的,紧跟着8086,为了克服在实模式下的种种问题,处理器厂商开发出保护模式,这样,物理地址不能直接被程序访问,程序内部的地址(虚拟地址)需要被转化成物理地址之后再去访问,程序对此一无所知,它只需要老老实实运行就是。

    32 位 CPU 具有保护模式和实模式两种运行模式,可以兼容实模式下的程序(一切以兼容为大,兼容 == money)。兼容实模式,是指能够正确处理好实模式下的程序,并不是说在实模式下运行时就完全变成了纯 16 位的 CPU。就像中学生做小学生的题一样,可以用小学生的知识方法来做,但并不要求自己退化成小学生的知识水平。如果不强调方法,甚至可以用中学知识来解小学生问题。

    假设我们使用的是32位的CPU,当开机时,32位的CPU是处于先处于实模式(bios -> mbr -> loader -> kernel -> os,这些寻址过程是要求准确物理地址的),之后再进入保护模式的(其它程序运行)。
    在保护模式下每运行一个实模式下的程序,就要为其虚拟一个实模式环境,故称虚拟模式。它是实模式向保护模式过渡时的产物(毕竟要搞兼容占住市场)。

    既然保护模式是与实模式相对区分开的,首先,先要能解决实模式下存在的问题。
    (1)实模式下的用户程序可以破坏存储代码的内存区域,所以要添加个内存段类型属性来阻止这种行为。
    (2)实模式下的用户程序和操作系统是同一级别的, 所以要添加个特权级属性来区分用户程序和操作系统的地位。
    其次,是一些访问内存段的必要属性条件。
    (1)内存段是一片内存区域,访问内存就要提供段基址,所以要有段基址属性。
    (2)为了限制程序访问内存的范围,还要对段大小进行约束,所以要有段界限属性。
    这些用来描述内存段的属性,被放到了一个称为段描述符的结构中,顾名思义,该结构专门用来描述一个内存段,该结构是 8 字节大小,结构如下图所示。

    段选择子

    段寄存器 CS、 DS、 ES、 FS、 GS、 SS,在实模式下时,段中存储的是段基地址,即内存段的起始地址。而在保护模式下时,由于段基址已经存入了段描述符中,所以段寄存器中再存放段基址是没有意义的,在段寄存器中存入的是一个叫作选择子的东西------------selector。
    选择子“基本上”是个索引值,这里说的是基本上,。用此索引值在段描述符表中索引相应的段描述符,这样,便在段描述符中得到了内存段的起始地址和段界限值等相关信息。
    下面正式介绍下选择子。由于段寄存器是 16 位,所以选择子也是 16 位,在其低 2 位即第 0~1 位,用来存储 RPL,即请求特权级,可以表示 0、 1、 2、 3 四种特权级。此处可以理解为请求者的当前特权级。
    选择子的第 2 位是 TI 位,即 Table Indicator,用来指示选择子是在 GDT 中,还是 LDT 中索引描述符。 TI为 0 表示在 GDT 中索引描述符, TI 为 1 表示在 LDT 中索引描述符。选择子的高 13 位,即第 3~15 位是描述符的索引值,用此值在 GDT 中索引描述符。前面说过 GDT 相当于一个描述符数组,所以此选择子中的索引值就是 GDT 中的下标。选择子结构如下图所示:
    在这里插入图片描述
    由于选择子的索引值部分是 13 位,即 2 的 13 次方是 8192,故最多可以索引 8192 个段,这和 GDT中最多定义 8192 个描述符是吻合的。
    选择子的作用主要是确定段描述符,确定描述符的目的,一是为了特权级、界限等安全考虑,最主要的还是要确定段的基地址。

    段描述符

    在这里插入图片描述
    保护模式下地址总线宽度是 32 位,段基址需要用 32 位地址来表示。
    段界限表示段边界的扩展最值,即最大扩展到多少或最小扩展到多少。扩展方向只有上下两种。对于数据段和代码段,段的扩展方向是向上,即地址越来越高,此时的段界限用来表示段内偏移的最大值。对于栈段,段的扩展方向是向下,即地址越来越低,此时的段界限用来表示段内偏移的最小值。无论是向上扩展,还是向下扩展,段界限的作用如同其名,表示段的边界、大小、范围。
    段界限用 20 个二进制位来表示。只不过此段界限只是个单位量,它的单位要么是字节,要么是 4KB,这是由描述符中的 G 位来指定的。
    最终段的边界是此段界限值*单位,故段的大小要么是 2 的 20 次方等于 1MB,要么是 2 的 32 次方(4KB 等于 2 的 12 次方, 12+20=32)等于 4GB。
    上面所说的 1MB 和 4GB 只是个范围,并不是具体的边界值。由于段界限只是个偏移量,是从 0 算起的,所以:
    实际的段界限边界值=(描述符中段界限+1) *(段界限的粒度大小: 4KB 或者 1) -1
    这个公式很简单,就是表示有多少个 4KB 或 1。由于描述符中的段界限是从 0 起的,所以左边第 1个括号中要加个 1,表示 4KB 或 1 的实际数量。它与第二个括号中的段粒度大小相乘后得到的乘积是以 1为起始的段的实际大小。由于地址是以 0 为起始的,所以公式的最后又减了 1。

    如果 G 位为 0,表示段界限粒度大小为 1 字节,根据上面的公式,实际段界限=(描述符中段界限+1)*1 -1=描述符中段界限,段界限实际大小就等于描述符中的段界限值。
    如果 G 位为 1,表示段界限粒度大小为 4KB 字节,故实际段界限=(描述符中段界限+1) 4k-1。
    举个例子,如果是平坦模型,段界限为 0xFFFFF, G 位为 1,套用上面公式,段界限边界值=0x100000
    0x1000-1=0xFFFFFFFF。
    段描述符的低 32 位分为两部分,前 16 位用来存储段的段界限的前 0~15 位,后 16 位用来存储段基址的 0~15 位。

    在段描述符的高 32 位中:
    0~7 位是段基址的 16~23, 24~31 位是段基址的 24~31 位, 加上在段描述符低 32 位中的段基址 0~15 位,这下 32 位基地址才算齐全了。

    8~11 位是 type 字段,共 4 位,用来指定本描述符的类型。这里要提前说下段描述符的 S 字段了。是这样的,一个段描述符,在 CPU 眼里分为两大类,要么描述的是系统段,要么描述的是数据段,这是由段描述符中的 S 位决定的,用它指示是否是系统段。

    段描述符的第 13~14 位是 DPL 字段, Descriptor Privilege Level,即描述符特权级,这是保护模式提供的安全解决方案,将计算机世界按权力划分成不同等级,每一种等级称为一种特权级。(保护模式下权限访问跟它有很大的关系)

    全局描述符表

    全局描述符表(Global Descriptor Table, GDT)是保护模式下内存段的登记表,这是不同于实模式的显著特征之一。

    一个段描述符只用来定义(描述)一个内存段。代码段要占用一个段描述符、数据段和栈段等,多个内存段也要各自占用一个段描述符,这些描述符放在哪里呢?答案是放在全局描述符表,为什么将该表称为“全局”描述符表?全局体现在多个程序都可以在里面定义自己的段描述符,是公用的。全局描述符表位于内存中, 需要用专门的寄存器指向它后, CPU 才知道它在哪里。这个专门的寄存器便是 GDTR,即 GDT Register,专门用来存储 GDT 的内存地址及大小。 如下图所示:
    在这里插入图片描述
    GDT界限表示它能存储的段描述符的多少,由于 GDT 的大小是 16 位二进制,其表示的范围是 2的 16次方等于 65536字节。每个描述符大小是 8字节,故,GDT中最多可容纳的描述符数量是 65536/8=8192个,即 GDT 中可容纳 8192 个段或门。
    要寻址到一个描述符可以用起始地址+段选择子内段描述符的index*8字节的方式找到段描述符,从而可以由该方式寻址到要找的物理段,只需要拥有该段描述符的编号。

    分页

    分页机制其实是建立在分段机制之上的。 这是不是有些让大家意外呢?其实这并不是说分页机制依赖于分段机制, 只是这内存分段机制属于 Intel IA32 架构骨子里的东西, 改是改不掉的, 除非从头再造个 CPU出来,所以分页机制只能在现有分段机制大局已定的情况下诞生。(在内存不足,且内存使用物理不连续时,需要置换部分内存出去,分段之下只能kill掉一部分进程,但是分页能让他在不置换的情况下运行)

    CPU 在不打开分页机制的情况下,是按照默认的分段方式进行的,段基址和段内偏移地
    址经过段部件处理后所输出的线性地址, CPU 就认为是物理地址。如果打开了分页机制,段部件输出的线性地址就不再等同于物理地址了,我们称之为虚拟地址,它是逻辑上的,是假的,不应该被送上地址总线(因为地址只是个数字,任何数字都可以当作地址,这里说的“不应该”是指应该人为保证送上地址总线上的数字是正确的地址)。 CPU 必须要拿到物理地址才行,此虚拟地址对应的物理地址需要在页表中查找,这项查找工作是由页部件自动完成的。为了要搞清楚页部件的工作原理,必须要搞清楚这两件事。
    在这里插入图片描述
    经过段部件处理后,保护模式的寻址空间是 4GB,注意啦,这个寻址空间是指线性地址空间,它在逻辑上是连续的。分页机制的思想是:通过映射,可以使连续的线性地址与任意物理内存地址相关联,逻辑上连续的线性地址其对应的物理地址可以不连续。
    分页机制的作用有两方面。
    1)将线性地址转换成物理地址。
    2)用大小相等的页代替大小不等的段。

    即使在分页机制下的进程也要先经过逻辑上的分段才行,每加载一个进程,操作系统按照进程中各段的起始范围,在进程自己的 4GB 虚拟地址空间中寻找可用空间分配内存段,此虚拟地址空间可以是页表,也可以是操作系统维护的某种数据结构,总之此阶段的分
    配是逻辑上的,并没有真正写入物理内存。

    一级页表

    页是地址空间的计量单位,并不是专属物理地址或线性地址,只要是 4KB 的地址空间都可以称为一页,所以线性地址的一页也要对应物理地址的一页。一页大小为 4KB,这样一来, 4GB 地址空间被划分成 4GB/4KB=1M 个页, 也就是 4GB 空间中可以容纳1048576 个页, 页表中自然也要有 1048576 个页表项,这就是我们要说的一级页表。
    在这里插入图片描述

    二级页表

    前面讲述了页表的原理,并以一级页表作为原型讲述了地址转换过程。既然有了一级页表,为什么还要搞个二级页表呢?理由如下。
    (1)一级页表中最多可容纳 1M(1048576)个页表项,每个页表项是 4 字节,如果页表项全满的话,便是 4MB 大小。
    (2)一级页表中所有页表项必须要提前建好,原因是操作系统要占用 4GB 虚拟地址空间的高 1GB,用户进程要占用低 3GB。
    (3)每个进程都有自己的页表,进程一多,光是页表占用的空间就很可观了。

    二级页表很好地解决了该问题。我们来说下什么是二级页表。
    无论是几级页表,标准页的尺寸都是 4KB,这一点是不变的。所以 4GB 线性地址空间最多有 1M 个标准页。一级页表是将这 1M 个标准页放置到一张页表中,二级页表是将这 1M 个标准页平均放置 1K 个页表中。每个页表中包含有 1K 个页表项。页表项是 4 字节大小,页表包含 1K 个页表项,故页表大小为4KB,这恰恰是一个标准页的大小。

    拆分出了这么多个页表,如何使用它们呢?为此,专门有个页目录表来存储这些页表。每个页表的物理地址在页目录表中都以页目录项(Page Directory Entry, PDE)的形式存储,页目录项大小同页表项一样,都用来描述一个物理页的物理地址,其大小都是 4 字节,而且最多有 1024 个页表,所以页目录表也是 4KB 大小,同样也是标准页的大小。
    页表是用于管理内存的数据结构,其也要占用内存,所以页目录表和页表所占用的物理页,同样混迹于物理内存之中,
    在这里插入图片描述
    每个页表中可容纳 1024 个物理页,故每个页表可表示的内存容量是 10244KB=4MB。页目录中共有1024 个页表,故所有页表可表示的内存容量是 10244MB=4GB,这已经达到了 32 位地址空间的最大容量。
    所以说,任意一个 32 位物理地址,它必然在某个页表之内的某个物理页中。我们定位某一个物理页,必然要先找到其所属的页表。页目录中 1024 个页表,只需要 10 位二进制就能够表示了,所以,虚拟地址的高10 位(第 31~22 位)用来在页目录中定位一个页表,也就是这高 10 位用于定位页目录中的页目录项 PDE,PDE 中有页表物理页地址。 找到页表后, 到底是页表中哪一个物理页呢?由于页表中可容纳 1024 个物理页,
    故只需要 10 位二进制就能够表示了。所以虚拟地址的中间 10 位(第 21~12 位)用来在页表中定位具体的物理页,也就是在页表中定位一个页表项 PTE, PTE 中有分配的物理页地址。由于标准页都是 4KB, 12 位二进制便可以表达 4KB 之内的任意地址,故线性地址中余下的 12 位(第 11~0 位)用于页内偏移量。
    经以上分析,二级页表地址转换原理是将 32 位虚拟地址拆分成高 10 位、中间 10 位、低 12 位三部分,它们的作用是:高 10 位作为页表的索引,用于在页目录表中定位一个页目录项 PDE,页目录项中有页表物理地址,也就是定位到了某个页表。中间 10 位作为物理页的索引,用于在页表内定位到某个页表项 PTE,页表项中有分配的物理页地址,也就是定位到了某个物理页。低 12 位作为页内偏移量用于在已经定位到的物理页内寻址。
    同一级页表一样,访问任何页表内的数据都要通过物理地址。由于页目录项 PDE 和页表项 PTE 都是4 字节大小,给出了 PDE 和 PTE 索引后,还需要在背后悄悄乘以 4,再加上页表物理地址,这才是最终要访问的绝对物理地址。转换过程背后的具体步骤如下。
    (1)用虚拟地址的高 10 位乘以 4,作为页目录表内的偏移地址,加上页目录表的物理地址,所得的和,便是页目录项的物理地址。读取该页目录项,从中获取到页表的物理地址。
    (2)用虚拟地址的中间 10 位乘以 4,作为页表内的偏移地址,加上在第 1 步中得到的页表物理地址,所得的和,便是页表项的物理地址。读取该页表项,从中获取到分配的物理页地址。
    (3)虚拟地址的高 10 位和中间 10 位分别是 PDE 和 PTE 的索引值,所以它们需要乘以 4。但低 12 位就不是索引值啦,其表示的范围是 0~0xfff,作为页内偏移最合适,所以虚拟地址的低 12 位加上第 2 步中得到的物理页地址,所得的和便是最终转换的物理地址。
    举个例子:比如 mov ax, [0x1234567],其过程如图所示:
    在这里插入图片描述

    实模式和保护模式的比较

    1,实模式可以直接访问物理地址;保护模式下不是你想访问哪个物理地址就能访问哪个物理地址,要通过分页,GDT等来进行间接访问。
    2,实模式没有特权级的概念,人人都可以占山为王,攻伐不断;保护模式有严格的权限保护(虽然这里没讲)。
    3,实模式下不可能用平坦模型(寄存器的锅),而保护模式 可以直接过渡到平坦模型。
    这里放的大部分内容来自郑刚大佬的《操作系统真象还原》,膜拜。

    展开全文
  • X86汇编语言从实模式到保护模式X86汇编语言从实模式到保护模式X86汇编语言从实模式到保护模式X86汇编语言从实模式到保护模式X86汇编语言从实模式到保护模式
  • 【OS学习笔记】十五 保护模式三:保护模式下的内存访问机制 按照顺序将上述四篇文章对应的内容学完,方能看懂本篇文章的汇编代码。 更加详细的介绍请参考数据《X86汇编语言-从实模式到保护模式...

    本文记录的是之前四篇文章所对应的汇编代码。四篇文章分别是:

    按照顺序将上述四篇文章对应的内容学完,方能看懂本篇文章的汇编代码。

    更加详细的介绍请参考数据《X86汇编语言-从实模式到保护模式》第11章内容。

    代码如下:

             ;代码清单11-1
             ;文件名:c11_mbr.asm
             ;文件说明:硬盘主引导扇区代码 
             ;
    
             ;设置堆栈段和栈指针 
             mov ax,cs      
             mov ss,ax
             mov sp,0x7c00
          
             ;计算GDT所在的逻辑段地址 
             mov ax,[cs:gdt_base+0x7c00]        ;16位 
             mov dx,[cs:gdt_base+0x7c00+0x02]   ;16位 
             mov bx,16        
             div bx            
             mov ds,ax                          ;令DS指向该段以进行操作
             mov bx,dx                          ;段内起始偏移地址 
          
             ;创建0#描述符(描述符是8字节的),它是空描述符,这是处理器的要求
             mov dword [bx+0x00],0x00
             mov dword [bx+0x04],0x00  
    
             ;创建#1描述符,保护模式下的代码段描述符
             mov dword [bx+0x08],0x7c0001ff     
             mov dword [bx+0x0c],0x00409800     
    
             ;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区) 
             mov dword [bx+0x10],0x8000ffff     
             mov dword [bx+0x14],0x0040920b     
    
             ;创建#3描述符,保护模式下的堆栈段描述符
             mov dword [bx+0x18],0x00007a00
             mov dword [bx+0x1c],0x00409600
    
             ;初始化描述符表寄存器GDTR
             mov word [cs: gdt_size+0x7c00],31  ;描述符表的界限(总字节数减一) 因为一共4个段(代码段数据段栈段与空段)  
                                                 
             lgdt [cs: gdt_size+0x7c00]				  ;从gdt_size读取6字节的话,就包括了4字节的段线性地址和2字节的偏移地址
          
             in al,0x92                         ;南桥芯片内的端口 
             or al,0000_0010B
             out 0x92,al                        ;打开A20
    
             cli                                ;保护模式下中断机制尚未建立,应 
                                                ;先禁止中断 
             mov eax,cr0
             or eax,1
             mov cr0,eax                        ;设置PE位,直接导致处理器的运行进入保护模式
          
             ;以下进入保护模式... ...
             jmp dword 0x0008:flush             ;16位的描述符选择子:32位偏移
                                                ;清流水线,并串行化处理器 
             [bits 32] 
    
        flush:
             mov cx,00000000000_10_000B         ;加载数据段选择子(0x10)
             mov ds,cx
    
             ;以下在屏幕上显示"Protect mode OK." 
             mov byte [0x00],'P'  
             mov byte [0x02],'r'
             mov byte [0x04],'o'
             mov byte [0x06],'t'
             mov byte [0x08],'e'
             mov byte [0x0a],'c'
             mov byte [0x0c],'t'
             mov byte [0x0e],' '
             mov byte [0x10],'m'
             mov byte [0x12],'o'
             mov byte [0x14],'d'
             mov byte [0x16],'e'
             mov byte [0x18],' '
             mov byte [0x1a],'O'
             mov byte [0x1c],'K'
    
             ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 
             mov cx,00000000000_11_000B         ;加载堆栈段选择子
             mov ss,cx
             mov esp,0x7c00
    
             mov ebp,esp                        ;保存堆栈指针 
             push byte '.'                      ;压入立即数(字节)
             
             sub ebp,4
             cmp ebp,esp                        ;判断压入立即数时,ESP是否减4 
             jnz ghalt                          
             pop eax
             mov [0x1e],al                      ;显示句点 
          
      ghalt:     
             hlt                                ;已经禁止中断,将不会被唤醒 
    
    ;-------------------------------------------------------------------------------
         
             gdt_size         dw 0
             gdt_base         dd 0x00007e00     ;GDT的物理地址 在主引导扇区之后
                                 
             times 510-($-$$) db 0
                              db 0x55,0xaa
    

    笔记记得不是很全,如果有不懂的可以加我联系方式一起交流。

    学习探讨加个人:
    qq:1126137994
    微信:liu1126137994

    展开全文
  • X86从实模式到保护模式 源代码。
  • 实模式 保护模式

    实模式【REAL MODE】

    1. 程序按照8086寻址方式访问1MB的空间
    2. 寻址方式:物理地址(20位)=段地址:偏移地址
    3. CPU单任务运行

    保护模式【PROTECT MODE】
    为存储器共享和保护以及实现虚拟存储器提供了硬件支持。

    1. 寻址方式:段(32位)和偏移量(32位),寻址4GB空间 (常规空间1MB,所以必须使用虚拟内存技术)
    2. 段的属性:起始地址,存取属性,权限级别
    3. 机制:段页式寻址
    4. 实现: CPU支持多任务并存
    5. 主要目的:保护多任务之间代码访问的完全隔离(使用虚拟地址)

    以上来自华中科技大学软件学院苏曙光老师操作系统原理课程

    有关保护模式的存储管理机制和保护机制的详细介绍:http://blog.sina.com.cn/s/blog_511703010100m1yr.html

    以下转载于:https://blog.csdn.net/qq_37653144/article/details/82818191

    从80386开始,CPU有三种工作模式:实模式、保护模式和虚拟8086模式。80286开始的CPU引入保护模式,实际上,实模式概念是在保护模式推出之后为了区别保护模式之前的8086CPU工作模式才有的,在8086时代CPU工作模式只有一种,自然没有实模式之说。实模式有着先天的缺陷(下文详述),但出于向上兼容的考虑,现代CPU仍然保持着对16位操纵模式的兼容。

    所谓工作模式,是指CPU的寻址方式、寄存器大小、指令用法和内存布局等。

    实模式
    实模式的“实”体现在程序中用到的地址都是真实的物理地址,“段基址:段内偏移地址”产生的逻辑地址就是物理地址,即程序员可见的地址完全是真实的内存地址。

    在实模式下,内存寻址方式和8086相同,由16位段寄存器的内容乘以16(左移4位)作为段基址,加上16位段偏移地址形成20位的物理地址,最大寻址空间1MB,最大分段64KB。可以使用32位指令,即32位的x86 CPU也可以兼容实模式,此时的实模式相当于高速的8086(32位CPU的实模式可以使用32位下的资源)。在32位CPU下,系统复位或加电时都是以实模式启动,然后再切换为保护模式。在实模式下,所有的段都是可以读、写和可执行的。

    下图是实模式下的内存访问模型:
    在这里插入图片描述
    8086CPU的实模式开创性地提出了地址分段的概念,改变了在它之前的CPU只能“硬编码”,程序无法重定位的缺点。然而实模式还是有很多缺陷,其中最主要的是实模式的安全隐患。在实模式下,用户程序和操作系统拥有同等权利,因为实模式下没有特权级。此外,程序可以随意修改自己的段基址,加上实模式下对地址的访问就是实实在在的物理地址,因此程序可以随意修改任意物理地址,甚至包括操作系统所在的内存,这给操作系统带来极大的安全问题。

    保护模式
    尽管在Intel 80286手册中已经提出了保护模式,但实际上它只是一个指引。80286虽然有了保护模式但其依然是16位的CPU,其通用寄存器还是16位宽,只不过其地址线由20位变成了24位,即寻址空间扩大到了16MB(但受限于寄存器位宽,单个寄存器的寻址空间仍然为64KB)。80286只是一个“过渡”产品,很快就被淘汰。

    真正的32位地址出现在Intel 80386上,它的地址总线和寄存器都是32位的,因此其单寄存器的寻址空间扩大到了4GB——在当时甚至其后的数年,仅通过段内偏移地址都足以访问内存的任意角落,这也开启了“平坦模型”的时代。

    保护模式本身是80286及以后的x86系列处理器产生的一种操作模式,它具有许多特性设计为提高系统的多道任务和系统的安全性及稳定性——例如内存的保护,分页机制和硬件虚拟存储的支持。现代多数的x86处理器操作系统都运行在保护模式下,包括Linux, Free BSD, 和Windows3.0(它也运行在实模式下,为了和Windows 2.x应用程序兼容)及以后的版本。

    在保护模式中,内存的管理模式分为两种——段模式和页模式。其中页模式也是基于段模式的。也就是说,保护模式的内存管理模式事实上是:纯段模式和段页式。进一步说,段模式是必不可少的,而页模式则是可选的——如果使用页模式,则是段页式,否则这是纯段模式。

    为了改进实模式下内存访问的不安全性,保护模式给内存段添加了段属性来限制用户程序对内存的操作权限。保护模式引入了全局描述符表(Global Descriptor Table,GDT),GDT的表项是描述段类型属性的数据结构——段描述符。GDT中的每一个段描述符都描述了一个内存段的基本属性,如段基址、段界限、类型、DPL等等。

    正是由于以上概念的提出,使得“段地址:段内偏移地址”的访问策略从实模式下对物理地址的直接映射变成了保护模式下对GDT或LDT的间接映射(如下图所示),进程在访问内存段(无论是数据段还是代码段)前都需要通过特权级检查。段属性的加入让用户程序对内存的访问不再“为所欲为”。
    在这里插入图片描述

    展开全文
  • 接下来,如果继续在保护模式工作,需要加载操作系统模块;如果要进入实模式,那么需要进行模式切换。 1.进入保护模式时的初始化操作 处理器能够进入到保护模式之前,需要操作系统加载和初始化软件必须在...
  • FreeDOS可以运行在实模式或保护模式下,在启动FreeDOS时有4种运行模式选择: 前两种运行在保护模式下, 后两种运行在实模式下。 根据How to tell whether your CPU is running in real mode or protected mode...
  • 从实模式到保护模式

    千次阅读 2019-04-13 16:25:29
    这节就实战编写loader实现从实模式到保护模式再返回实模式 目录 1、从实模式到保护模式 1、从计算机的历史谈起 2、CPU历史的里程碑 - 8086 3、80286的登场 4、初识保护模式 5、80386的登场(计算机新时期的...
  • 实模式、保护模式和虚拟8086方式(详细) 热度 14已有 471 次阅读2011-3-12 16:41 |个人分类:技术 1:实模式:寻址采用和8086相同的16位段和偏移量,最大寻址空间1MB,最大分段64KB。可以使用32位指令。32...
  • x86架构实模式和保护模式 0. 术语释义 实模式——real model 保护模式——protected model 1. 概述 保护模式又称虚拟地址保护模式,与之相对的是实模式。 保护模式是在80286系列开始实现的一种CPU操作模式。在此之前...
  • 实模式和保护模式的简单对比。
  • 保护模式到实模式

    2016-09-03 11:13:36
    1、程序开始时在实模式下要有自己的堆栈段,进入保护模式前先暂存ss及sp的值至某内存处,以便从保护模式返回实模式后恢复到原先的堆栈。  2、返回实模式前需把各段寄存器设置为规范段,包括SS也要设置  3、返回...
  • 实模式保护模式区别  从80386开始,cpu有三种工作方式:实模式,保护模式和虚拟8086模式。只有在刚刚启动的时候是real-mode,等到linux操作系统运行起来以后就运行在保护模式。    实模式只能访问地址在1M以下...
  • 一 Eureka的自我保护模式 进入自我保护模式最直观的体现就是Eureka Server首页的警告,如下图: 技术分享 默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例...
  • 实模式与保护模式

    2018-03-21 22:15:25
    实模式与保护模式我认为有以下不同:实模式便是直接物理地址访问,即本身程序的地址便是机器真实的物理地址。保护模式是间接地址访问。现有的机器一般都是32//64位地址线,而可访问的地址空间一般都能达到500GB甚至1...
  • 本篇文章学习如何进入保护模式,并学习如何在保护模式下进行内存访问。 1、如何进入保护模式 假设我们已经用汇编语言将段描述符安装到GDT中(具体的汇编代码在后面的文章中会给出),并且也已经将GDTR的线性地址与...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,524
精华内容 8,209
关键字:

保护模式