保护模式

简介: 保护模式

从实模式到保护模式

  • 关于实模式和保护模式,要从历史谈起
  • 实模式可以理解为直接操作物理内存的模式
  • 初始时期的程序开发是直接操作物理内存的
  • CPU 指令的操作数直接使用实地址(实际内存地址)
  • 程序员拥有绝对的权利(利用 CPU 指哪打哪),这么做带来的问题
  • 难以重定位,程序每次都需要同样地址的内存执行
  • 给多道程序的设计带来了障碍,不同程序之间使用的内存绝对不能有重叠

CPU 历史的里程碑 - 8086

  • 地址位宽为 20 位,可以访问 1M 内存空间
  • 引入 [段地址:偏移地址] 的内存访问方式
  • 8086 的段寄存器和通用寄存器为 16 位
  • 单个寄存器最多访问 64K 的内存空间
  • 需要两个寄存器的配合,完成所有内存空间的访问
  • 更多内容见 【8086汇编基础】
  • 一个问题:[段地址:偏移地址] 能访问的最大地址为:0xFFFF:0xFFFF,即:10FFFF,那么,超过 1M 的空间,CPU 如何处理?
  • 由于 8086 只有 20 位地址线,因此,高位被丢弃(溢出)
  • 8086 中的高端地址区(High Memory Area)
0xFFFF:0xFFFF
  0xFFFF0 + 0xFFFF
    0xFFFF0 + (0xF + 0xFFF0)
      (0xFFFF0 + 0xF) + 0xFFF0
  • 8086地址线宽度是20,最多可以访问 1M 的内存空间。 而 (0xFFFF0 + 0xF) 正好2(20) = 1M ,已经就是8086处理器可以访问到的最大地址值了。此时多出来的 0xFFF0 是 8086 中的 无效地址范围,又
• 叫 高端内存区(HMA)。例如 0xFFFF:0xFFFF 实际访问的地址是 0xFFEF。
0xFFFF:0xFFFF
    |
100001111111111101111
    |
00001111111111101111 // 回卷,只保留低 20 位,高位被丢弃
    |
0xFFEF
  • 8086 时期应用程序的问题
  • 1M 内存完全不够用
  • 开发者在程序中大量使用回卷技术(HMA 地址被使用)
  • 应用程序之间没有界限,相互之间随意干扰,程序指哪打哪,内存任意地方都能修改

80286 登场

  • 8086 已经有那么多应用程序了,所以必须兼容
  • 加大内存容量,增加地址线数量(24 位)
  • [段地址:偏移地址]的方式可以强化一下
  • 为每一个段提供更多属性(如:范围,特权级等)
  • 为每个段的定义提供固定方式
  • 80286 的兼容性
  • 默认情况下完全兼容 8086 的运行模式(实模式)
  • 通过特殊的方式访问 1MB+ 的内存空间

初识保护模式

  • 这个特殊的方式指的是什么?
  • 80286 之后的工作模式 - 保护模式
实模式 保护模式
兼容 8086 的工作模式 新的工作模式
实地址 = (段寄存器 << 4) + 偏移地址 内存地址 = 段起始地址 + 偏移地址
任意内存随意访问 每个段增加各种属性,保证安全性
  • 保护模式为每个段增加各种属性,于是引出段描述符、段描述符表、段选择符三个概念

段描述符

  • 每一段内存拥有一个属性定义(段描述符 Descriptor)
  • 段描述符是 GDT 和 LDT 中的一个数据结构项,用于向处理器提供有关一个段的位置、大小以及访问控制的状态信息。每个段描述符的长度是 8 个字节,含有 3 个主要字段:
  • 段基地址
  • 段限长
  • 段属性
  • 段描述符分数据段、代码段以及系统段三种
  • 下图是段描述符通用格式

  • 有个疑问?为啥基地址,段限长要分成多个部分,而不是连续排列呢?这个只能说是历史原因,硬件决定,软件不能更改。

段限长字段 LIMIT(Segment limit)

  • 用于指定段的长度。处理器会把段描述符中两个段限长字段组合成一个 20 位的值,并根据颗粒度标志 G 来指定段限长 Limit 值的实际含义。
  • 如果 G=0,则 Limit 值的单位是 B,也就是说 Limit 的范围可以是 1B 到 1MB;
  • 如果 G=1,则 Limit 值的单位是 4KB,也就是说 Limit 的范围可以是 4KB 到 4GB。
  • 根据段类型字段 TYPE 中的段扩展方向标志 E,处理器可以以两种不同的方式使用 Limit。
  • E=0:表示向上扩展的段(简称上扩段),逻辑地址中的偏移值范围可以从 0 到 Limit;
  • E=1:表示向下扩展的段(简称下扩段),逻辑地址中的偏移范围可以从 Limit 到 0xFFFF(当 B=0 时)或者 0xFFFFFFFF(当 B=1 时)。关于B位,后面将解释。

基地址字段 BASE(Base address)

  • 该字段定义在 4GB 线性地址空间中一个段的字节 0 所处的位置(段起始位置)。
  • 段基地址可以是 0~4GB 范围内的任意地址(这同实模式不同,实模式下段基地址要求 16 字节对齐),但是,为了让程序具有最佳性能,还是建议段基地址对齐 16 字节边界。

段类型字段 TYPE

  • 该字段用于指定段或者门(Gate)的类型、说明段的访问种类以及段的扩展方向。该字段的解释依赖于描述符类型标志 S;TYPE 字段的编码对代码段、数据段或者系统描述符都不同。
代码和数据段描述符类型
  • 当段描述符中 S(描述符类型)标志为 1 时,则该描述符用于代码或数据段

系统描述符类型
  • 当段描述符中的 S 标志(描述符类型)为 0 时,则该描述符是一个系统描述符。处理器能够识别以下一些类型的系统段描述符:
  • 局部描述符表(LDT)的段描述符
  • 任务状态段(TSS)描述符
  • 调用门描述符
  • 中断门描述符
  • 陷阱门描述符
  • 任务门描述符
  • 这些描述符类型可分为两大类:系统段描述符和门描述符。系统段描述符指向系统段(如 LDT 和 TSS 段),门描述符就是一个“门”,对于调用、中断或陷阱门,其中含有代码段的选择符和段中程序入口点的指针;对于任务门,其中含有 TSS 的段选择符。下图给出了系统段描述符和门描述符类型字段的编码。

描述符类型标志 S(Descriptor type flag)

  • 描述符类型标志 S 指明一个段描述符是系统段描述符(当 S=0)还是代码或数据段描述符(当 S=1)

描述符特权级字段 DPL(Descriptor privilege level)

  • DPL 字段指明描述符的特权级。特权级范围从 0 到 3。0 级特权级最高,3 级最低。DPL 用于控制对段的访问

段存在标志 P(Segment present)

  • 用于指出一个段是在内存中(P=1)还是不在内存中(P=0)
  • 当一个段描述符的 P 标志为 0 时,那么把指向这个段描述符的选择符加载进段寄存器将导致产生一个段不存在异常。内存管理软件可以使用这个标志来控制在某一给定时间实际需要把那个段加载进内存中。这个功能为虚拟存储提供了除分页机制以外的控制。

D/B(默认操作大小/默认栈指针大小和/或上界限)标志

  • D/B 位对不同的段有不同的影响
  • 对数据段(CS)的影响:
  • D = 1,采用 32 位寻址方式
  • D = 0,采用 16 位寻址方式
  • 对栈段(SS)的影响:
  • D = 1,隐式对栈访问指令(会修改 ESP,如:PUSH、POP、CALL)使用 32 位堆栈指针寄存器 ESP
  • D = 0,隐式对栈访问指令(会修改 SP,如:PUSH、POP、CALL)使用 16 位堆栈指针寄存器 SP
  • 对向下扩展的数据段的影响:
  • D = 1,段上限为 4GB
  • D = 0,段上限为 64KB

颗粒度标志 G(Granularity)

  • 该字段用于确定段限长字段 Limit 值的单位。
  • 如果 G=0,则 Limit 值的单位是 B;
  • 如果 G=1,则 Limit 值的单位是 4KB;
  • 注意:这个字段不影响段基地址的颗粒度,基地址的颗粒度总是以字节为单位。

数据段、代码段、系统段描述符

  • 段描述符分为数据段、代码段以及系统段三种描述符,它们之间的差异如下图所示

段描述符表(Descriptor Table)

  • 什么是段描述符表?通俗的讲就是由多个段描述符组成的一个数组
  • 一个段描述符就占用 8 个字节,更别说整个段描述符表了,CPU 的寄存器肯定是放不下的,所以段描述符表是处于内存中。我们使用一个特殊寄存器来指向它,这样子我们就可以通过这个特殊寄存器来使用描述符表啦

段选择符

  • 我们在操作数组的时候需要两个要素,一个是数组首地址(数组名),另一个就是数组下标,段描述符表可以看作一个数组,段选择符就可以看作是这个数组下标,当然,它不仅仅是数组下标这么简单
  • 段选择符(或称段选择子)是段的一个 16 位标识符。段选择符并不直接指向段,而是指向段描述符表中定义段的段描述符。

  • 段选择符 3 个字段内容:
  • 请求特权级 RPL(Requested Privilege Level):请求者特权级标志,通过特权级标志判断是否可以访问对应段
  • 表指示标志 TI(Table Index):TI=0 表示描述符在 GDT 中;TI=1 表示描述符在 LDT 中
  • 索引值(Index):索引字段给出了描述符在 GDT 或 LDT 表中的索引项号

进入保护模式的方式

  1. 定义描述符表
  2. 打开 A20 地址线
  3. 加载描述符表
  4. 通知 CPU 进入保护模式

小结

  • [段地址:偏移地址] 的寻址方式解决了早期程序定位难得问题
  • 8086 实模式下的程序无法保证安全性
  • 80286 中提出了保护模式,加强了内存的安全性
  • 出于兼容性的考虑,80286 之后的处理器都有 2 中工作模式
  • 处理器需要特定的步骤才能进入保护模式,默认为实模式

80286 的光荣退场

  • 80286 的历史意义:引入了保护模式,为现代操作系统和应用奠定了基础
  • 808286 的葩设计
  • 段寄存器为 24 位,通用寄存器为 16 位(不伦不类)
  • 理论上,段寄存器中的数值可以直接作为段基址
  • 16 位通用寄存器最多访问 64K 的内存空间
  • 为了访问 16M 内存,必须不停切换段基址

80386 的登场

  • 80286 这款处理器属于个试水产品,所以很快就退出了历史舞台
  • 计算机新时期的标志 - 80386
  • 32 位地址总线(可支持 4G 的内存空间)
  • 段寄存器和通用寄存器都为 32 位。任意一个寄存器都能访问到内存的任意角落,这就相当于 4G 的内存空间可以看成一个段,段基址为 0, 使用通用寄存器可以访问整个 4G 内存空间 这就是所谓的平坦模式。

新时期的内存使用方式

  • 实模式:兼容 8086 的内存使用方式(指哪打哪)
  • 分段模式:通过 [段基址:偏移地址] 的方式将内存从功能上分段(数据段,代码段)
  • 平坦模式:所有内存就是一个段 [0:32 位偏移地址]
目录
相关文章
|
4月前
|
编译器 程序员
进入保护模式
进入保护模式
32 0
|
4月前
|
Linux
保护模式中的特权级
保护模式中的特权级
26 0
|
4月前
|
存储 NoSQL 安全
保护模式下的中断
保护模式下的中断
48 0
|
7月前
|
C语言 Windows
[笔记] Windows内核课程:保护模式《一》保护模式
[笔记] Windows内核课程:保护模式《一》保护模式
|
7月前
|
Windows
[笔记] Windows内核课程:保护模式《二》段寄存器介绍
[笔记] Windows内核课程:保护模式《二》段寄存器介绍
|
8月前
|
编译器 调度 C语言
【学习笔记】小 O 带你掌握操作系统的心跳 - OneOS 内核启动
一、简介 内核启动介绍了整个系统从硬件上电如何一步步进入用户程序的过程。一般情况下,启动过程分为硬件上电,首先运行和体系架构相关的启动汇编文件,进行一些最基本硬件的初始化 (例如 CPU 配置,时钟,栈地址,RAM 等),为内核运行铺垫好环境,然后初始化内核各模块 (例如调度器,定时器等),接着创建系统任务 (例如空闲任务) 和用户任务,最后启动调度和运行用户程序。
87 0
|
12月前
|
编解码 编译器
4.1保护模式
4.1保护模式
111 0
|
Linux
自制操作系统Antz day02——进入保护模式 (上) jmp到保护模式
最近在看操作系统底层方面的东西,最开始的为什么是07c00h这个问题就让我对操作系统有了很大的兴趣。所以准备在看书之余顺便写一个操作系统(Anz)。至于为什么这个系统会被叫做Antz,可以参考Antz Uhl Kone, 日语为アインズ·ウール·ゴウン , 与之对应的还有接下来准备写的自制脚本语言A.
1459 0
|
存储 缓存 IDE
自制操作系统Antz day04——进入保护模式 (下) 实现内核并从硬盘载入
目前已经完成了MBR的雏形,并且直接操作显卡完成了屏幕的内容显示。接下来我们要改造之前的MBR,做一个大的改进,使MBR可以读取硬盘,因为我们的MBR受限制于512字节大小,在这么小的空间里没法为内核准备好环境,更不要说加载内核到内存中并运行了,所以我们需要在另一个程序中完成初始化环境与加载内核的任务,这个程序我们叫做loader。
1504 0
|
存储 Linux 芯片
自制操作系统Antz day03——进入保护模式 (中) 直接操作显存
目前已经完成了MBR的雏形,虽然有些简陋,比如我们的屏幕显示还是使用的BIOS中断,而在BIOS中断向量表只有在实模式下存在 ,我们要进入保护模式之后就无法使用了。此次我们要完成直接操作显存来进行屏幕显示。
1770 0