2_80x86 处理器与保护模式初步

80x86 处理器与保护模式初步

16 位 80x86 微处理器

8086, 讲烂了, 这里就不说了.

80386 有 3 种工作模式

  • 实模式

相当于一个可进行 32 位快速运算的 8086

  • 保护模式

是 80x86 设计目标全部达到的工作模式, 通过对程序使用的存储区采用分段, 分页的存储管理机制, 达到分级使用, 互不干扰的保护目的. 能为每一个任务提供一台虚拟处理器, 使得每个任务单独执行, 快速切换.

  • 虚拟 8086 模式

保护模式下同时模拟多个 8086 处理器

32 为微处理器的寄存器

80x86 微处理器从 16 位升级为 32 位后, 它的寄存器也对应升级为 32 位

8个通用寄存器 (eax, ebx, ecx, edx, esi, edi,ebp, esp)

指令寄存器扩展为 32 位, EIP

6 个段寄存器 (CS, DS, SS, ES, FS, GS)

段寄存器长度均为 16 位, 其中 13 位代表内存段的一个编号, 成为 "段选择器"

就是说, 我把程序运行的时候, 占据了一段连续的内存空间, 这个连续的空间实际上在运行的时候是分段的, 每一段都有一个区分, 段怎么区分呢 ? 就是通过段寄存器来区分.

那么段寄存器怎么个区分法呢, 看下面.

保护模式下的 80x86 (段模式)

这个图大致给出个原理

保护模式下的 80x86模式, 也就是段模式. 实际上还有页模式, 页模式在以后 MIPS 讲.

我们这里就讲下段模式.

  • 支持多任务处理功能

  • 支持虚拟存储器特性

什么意思呢, 我们写程序的时候, 程序里面会有地址, 就是指针, 这个地址称之为逻辑地址.

逻辑地址由两个要件构成, 一个是 selector, 叫作段选择器. 第二就是 offset, 就是段选择器确定了你要访问的这个 memory 在哪个段, 段是个连续空间, 段内由 offset 来表示, 我需要把这个逻辑地址啊转换成一个物理地址.

那么为什么做这个转换呢. 因为保护模式下, 每个程序所看到的逻辑地址空间都是一样的, 但是我通过硬件, 通过处理器, 把你这个逻辑地址转换成分离的物理地址, 这样就会形成那个保护的效果了.

我们程序运行的时候, 看起来是访问一段连续的内存地址空间, 但实际上虚拟内存映射到实际的物理地址空间, 把它映射都分开了, 起到保护, 不会相互干扰的作用.

那么怎么区分开呢 ? 首先它要做一个虚实地址的转换, 相当于说在保护模式下有个存储器的寻址, 在这种方式下, 你的虚拟内存地址两个要素, 一个 selector, 一个 offset, offset 在这个变换中它是不变的, selector 起到关键的作用.

selector 叫作段寄存器, 严格来说它应该叫作段选择址. 相当于它告诉你, 有了这个 selector, 你怎么从一个表里头, 就是专门有个段描述符的一个表, 我在这个段描述符的这个表里面, 根据你的段选择器, 把你的这个程序所对应的这一段物理的起始地址, 就是 Base Address 给它提取出来.

这 Base Address 加上你的 offset, 两个一加起来生成的最后的那个物理地址. 物理地址就是说你通过地址总线, 访问内存时候的那个地址.

保护模式保护什么

分清不同任务使用的存储区域, 不允许随便使用其它任务的数据和代码.

必要条件 :

  1. 要标记每段存储区的所有者或被使用的权限级别
    一般来说, 不同的进程, 实际上就是不同的所有者, 你这个进程访问这段区域, 他这个进程访问另外一个区域, 大家从逻辑地址上看没什么区别, 但物理地址上, 就是根据刚才图上选择的那个结果, 大家就分布得不一样了

  2. 要标记使用者是谁 (权限级别)

  3. 中间环节 : CPU 要去判断此次访问是否合法
    程序虚拟内存地址转换成物理地址的时候, CPU 要进行 check

  • 在 X86-32 体系结构的保护模式下, 一个内存地址是由段基地址, 偏移地址两个要素构成的. 就相当于说 offset 两个要素构成的.

一个内存的段基地址是通过段描述符来获得的.

段描述符

  • 段描述符由三个要素构成--段基地址 (32位); 段长度 (20 位, 段长度单位为 2^12); 访问权限

段基地址就是刚才那个要加上你的偏移地址, 再最后生成你要访问的一个物理地址. 物理地址 = 段基地址 + 偏移地址.

段长度, 当然就是说你访问长度有限.

访问权限就是说, 你这个区域是可读可写还是什么的.

所以段描述符有一定长度, 它是有 64 位的.

  • 由于系统兼容性的原因, 段寄存器只有 16 位, 如何表示 64 位的段描述符 ?

就相当于我把整个系统的段描述符, 相关描述符放在段描述表里面, 有专门几个表存放段描述符.

那么我怎么去访问这个段描述符呢 ? 我就把现有的 16 位段寄存器的高 13 位, 作为索引来访问该表, 从而获得 64 位的段描述符.

描述符表分为两类

  • 一类是 GDT, 全局秘书负表, 主要存放操作系统和各个任务公用的描述符

包括公用的数据段, 代码段描述符, 各任务的 TSS 描述符和 LDT 描述符.

其中 TSS 是任务状态段, 存放各个任务私有运行状态信息描述符.

  • LDT 就是局部描述符表, 主要存放各个任务的私有描述符

那我们怎么去寻找这个 GDT 表, 全局描述符表呢 ?

有一个应用寄存器, 叫作 GDTR. 这里面所存放的就是全局描述符表的起始地址.

于是我们可以通过这个 GDTR 来获得全局描述符表的内存地址.

然后你用这个全局描述符表, 再用刚才那个段寄存器里面的高 13 位, selector 把表里面的一项提取出来, 把里面的段基址取出来再加上你的 offset, 就形成了最终的物理地址.


LDT 也是类似, 但是 LDT 有个不一样的地方.

LDT 有好多个, 因为当前任务有多少个, 你 LDT 就会有多少个嘛. 这点不一样.


  • 段寄存器 : 高 13 位用来指示描述符在描述符表中的索引号, 低 2 位是表示使用描述符的特权级别.

具体过程见下图

T1 = 0 描述符 n 给出目的地址的基址与限长

访问全局描述符表的过程

T1 = 1 描述符 x 给出目的地之的基址与限长

访问局部描述符表的过程

寄存器与存储器的比较

项目 寄存器 存储器
位置 在 CPU 内部 在 CPU 外部
访问速度
容量
成本
表示方式 用名字表示 用地址表示
地址 没有 可用多种方式形成