首页 > 其他分享 >实模式、保护模式和长模式

实模式、保护模式和长模式

时间:2024-08-11 15:04:57浏览次数:7  
标签:8086 保护模式 操作系统 模式 地址 内存 寄存器

个人一直对硬件、操作系统等底层技术感兴趣,无奈x86架构实在过于复杂,虽然国内外很多计算机通识教育已经将主要平台迁移至更简单的risc-v,但不可否认,很多优秀的参考资料依旧是基于x86的。当你打开这些资料,一大堆眼花缭乱的新名词直接砸到你脑袋上,什么实模式、保护模式、长模式、段寄存器、平坦模型等等等。

不难猜到,这些名词大概都是由历史原因导致的,每一个名词的背后一定有其被创造出来的原因以及解决的具体问题,但没人会讲这些历史,只是机械的告诉你打开保护模式会怎样,和实模式有啥区别等等,这让人难以接受。

所以我决定尝试理解这些内容,并写出一篇从历史角度出发来解释这些名词的文章。

从8086开始

1978年诞生,16位、支持1MB内存寻址空间

8086是下x86架构下的第一个CPU,我从网上随便掏出来一张它的内部结构图,可以看到其内部结构是非常非常简单的,比较接近大学的课程中讨论的CPU:

img

简单说一下,CPU大概可以分为几个部分:控制器负责指令执行过程中整个流程的控制;运算器(ALU)则负责执行指令中的计算部分;寄存器是运算的参数或结果存放的位置;通过外部总线来读取内存。

CPU一条指令的执行过程大概有:取指令、指令译码、执行指令、访问内存(非必须)、写回(非必须)

上图中,左侧的内容和我们在教科书中接触到的CPU几乎一致,右侧这个内部暂存器(CS、DS、SS、ES)和地址加法器我们不知道是啥,一会再说。

从这张简单的图来看,8086中并没有什么MMU、TLB等器件,这是CPU用于支持现代操作系统实现虚拟内存的核心。而经过查阅,8086几乎都被用在嵌入式系统中,它的姊妹8088被IBM用在了第一代个人计算机IBM PC上,而该计算机搭载的IBM PC DOS 1.0操作系统是一个单任务的命令行操作系统,所以自然没有进程的概念,也不需要虚拟内存在多个并发的进程间进行内存隔离。

虽然没有虚拟内存支持,但从右上角也可以看出,8086的地址还是经过了一些转换的,比如指令中的0x12这个地址,经过地址加法器会被映射成另一个地址。这就是常说的段寻址,我们下面来从历史角度理解它。

从8086引出的段寻址

不知道你是否还记得上文中提到8086是16位CPU,一个16位的地址空间有多大呢?只有可怜的65535字节,也就是64KB。同时上文还提到了8086支持1MB的寻址空间,这是怎么做到的呢?

下文是GPT给我的讲解:

想象你住在一个大型公寓大楼里,这栋大楼里有许多套房子,而每套房子都有一个编号,比如“01室”、“02室”等。如果每个房子只能有两位数字的编号(如01到99),那么你最多只能有99套房子。

但现在,假设公寓的设计者想容纳更多的房子,而不是重新设计整个编号系统。他们决定将公寓划分为几个“楼层”,每个楼层有自己的编号范围。于是,每个房子的地址就变成了“楼层号 + 房子号”,比如“2楼的08号房子”就是“208室”。

  • 楼层号:类似于8086中的段寄存器。
  • 房子号:类似于8086中的偏移量。

如果公寓有10个楼层,每个楼层有99套房子,那么总共可以容纳990套房子。尽管房子号仍然只有两位数字(最多99),但通过引入楼层号(段寄存器),你大幅扩展了公寓的容量。

段寻址的思想就是利用一个寄存器作为段的选择器,你往这个寄存器中写值就是在选择段,最终的地址为段选择器的值左移4位(乘以16)得到20位的段基址,加上指令中给定的16位物理内存地址,最终得到20位的内存地址,即1MB的寻址空间。

这种设计引入了一定复杂性,但也许是当时最好的选择了吧。

实模式(real mode / real address mode)

8086工作的方式就被称作实模式,这是在后面有了更高级的设计之后为了区别新的寻址方式而创造出来的名词。

其实有了上面的历史知识,理解实模式变得无比自然,实模式中没有虚拟地址转换,程序中指定的地址就是物理地址。

为了应对16位地址空间满足不了需求的问题,x86又造出了复杂的段寻址,x86中实际的地址是楼层号(段寄存器)与房间号(地址)的结合,而机器代码中的地址只不过是在段内的偏移量。

80286和保护模式

我们已经介绍了8086CPU的历史,从现在的视角来看,那真是一个古老物件了,要想实现现代操作系统,8086缺失了很多功能:

  1. 内存保护机制:实模式不存在内存保护,无法建立隔离的内存空间。现代操作系统依赖其在多个任务之间提供内存隔离。
  2. 多任务处理能力缺失:没有一个简单的结构可以给操作系统来实现多任务处理。
  3. 无特权级别:现代操作系统通过特权级别来隔离内核态和用户态,保证系统的安全、稳定

这些在80286上通过一种被称作保护模式的机制解决,同时为了兼容旧软件,x86CPU reset时一定是处于实模式的,需要转到保护模式。

内存保护——进阶的分段机制

实模式的分段很简单,即段基址 + 偏移量,但对于段基址和偏移量没有任何限制,代码可以通过组合两个变量访问任何内存空间。

保护模式下,段寄存器中不再直接存储段基址,而是存储一种称作段选择器(Segment Selector)的东西,更专业的说法是段选择子,我觉得这个命名垃圾,所以我直译了,读者在其它资料上看到段选择子知道是一个东西就好。

段选择器是一个位置信息,这个位置就是选择的段对应的描述符位置。描述符被存储在内存中,有一个全局描述符表(GDT)和一个局部描述符表(LDT)。

有趣,我们要通过段寄存器中存储的值和机器代码中的偏移量来计算出最终的内存位置,但在这之前却要先访问内存读出段描述符才能继续

段描述符中包含了:

  • 段基址:描述段的实际起始位置
  • 段大小:描述段的大小,确保最终段基址 + 偏移量不会超出该段,从而达到段间隔离
  • 访问权限:权限控制
  • 其它属性:...

下图是一个内存中的段描述符布局,但这并非80286的,80286的要更加简单:

img

该图片来自极客时间的《操作系统实战45讲》,作者彭东,直达链接:https://time.geekbang.org/column/article/375278

好了好了,忽略一切细节,在保护模式下的内存访问流程如下:

  1. 读取段选择器
  2. 查找段描述符表(GDT、LDT),拿到段基址、限长、权限等属性
  3. 段基址 + 偏移量计算实际物理地址
  4. 校验权限
  5. 访存
  6. 异常处理,如访问越界(物理地址超出段边界)

段描述符表是谁设置的呢?当然是运行在其上的操作系统,在从实模式进入到保护模式之前,操作系统必须设置号GDT,并将段选择器设置妥当,再启用保护模式

特权级别

在实模式下,任何指令都可以无差别的被执行,这在现代操作系统上是很危险的,因为一个坏人可以轻易的摧毁你的系统。

保护模式提供了四个特权级别,分别为R0到R3,权限依次减少:

img

该图片来自极客时间的《操作系统实战45讲》,作者彭东,直达链接:https://time.geekbang.org/column/article/375278

平坦模型——我脑子不够用了,来点简单的吧

我一直在说分段,也讲了实模式和保护模式下的分段规则,但我一直没有阐述细节,比如使用哪些寄存器作为段寄存器,我也没有拿出一个实际的例子来计算,因为我脑子不行。

通过精心设计GDT,比如我们把段基址设置成0,段限长设置为4GB,此时系统中只有一个段,该段的大小就是全部的物理地址空间。这样可以简化内存的管理,告别地址转换。

标签:8086,保护模式,操作系统,模式,地址,内存,寄存器
From: https://www.cnblogs.com/lilpig/p/18353172

相关文章

  • 【Redis进阶】缓存设计模式
    目录CacheAside(旁路缓存)模式概念读操作流程如上图所示写操作流程如上图所示代码示例总结Read-Through模式概念操作流程:优点:Write-Through模式概念操作流程:优点:Write-Behind(Write-Back)模式概念操作流程:优点:缺点:总结缓存设计模式是指将缓存作为系统架......
  • [纯干货]SpringCould + 适配器模式 + nacos动态部署 OSS 对接
    一、前言在一个微服务项目里,我们的OSS云存储服务常常需要配置诸如阿里云、腾讯云、minio等多个云存储厂商的业务代码,而且后续无法确保是否会增添新的云存储厂商。此时,倘若我们要修改具体使用的云存储厂商,就会致使controller层和service层发生变动,这并不符合低耦合的理......
  • Intel系列微处理器的3种工作模式
    Intel系列微处理器的3种工作模式​微机中常用的Intel系列微处理器的主要发展过程是:8080,8086/8088,80186,80286,80386,80486,Pentium,PentiumⅡ,PentiumlII,Pentium4​8086/8088是一个重要的阶段,8086和8088是略有区别的两个功能相同的CPU。8088被IBM用在了它所生产的......
  • C--设计模式-全-
    C#设计模式(全)原文:DesignPatternsinC#协议:CCBY-NC-SA4.0一、单例模式这一章涵盖了单例模式。GoF定义确保一个类只有一个实例,并提供对它的全局访问点。概念让我们假设您有一个名为A,的类,您需要从它创建一个对象。一般情况下,你会怎么做?您可以简单地使用这一行代码:A......
  • 设计模式之策略模式
    策略模式在策略模式(StrategyPattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户......
  • 设计模式——单例设计模式
    单例设计模式如果你想创建一个类而且它在任何时刻只会有一个对象,那么你就应该使用单例类模式。此模式保证某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。懒汉式:只有调用getInstance方法时才创建对象.使用synchronized同步锁来保证防止多个线程同时调......
  • 工厂模式与策略模式的区别及其在Java中的应用
    工厂模式与策略模式的区别及其在Java中的应用1.引言在软件开发中,设计模式被广泛应用于解决各种常见问题,提高代码的可维护性、可扩展性和可读性。工厂模式(FactoryPattern)和策略模式(StrategyPattern)是两种非常重要的设计模式,它们解决了不同的设计问题,并且在许多情况下可......
  • Java设计模式:中介者模式详解与最佳实践
    Java设计模式:中介者模式详解与最佳实践1.引言在软件开发过程中,特别是复杂系统的构建中,模块间的交互往往成为影响代码质量的重要因素。当模块之间耦合度过高时,系统的维护、扩展和理解成本都会显著增加。为了降低模块之间的耦合度,保持系统的灵活性和可维护性,我们可以使用设......
  • [设计模式]工厂模式
    简单工厂//计算类的基类@Setter@GetterpublicabstractclassOperation{privatedoublevalue1=0;privatedoublevalue2=0;protectedabstractdoublegetResule();}//加法publicclassOperationAddextendsOperation{@Overrideprote......
  • 工厂模式【简单工厂工厂方法抽象工厂】
    简单工厂:将对象的创建封装在子类当中,客户不需要去手动创建对象,只需要调用一些接口传递不同的类型参数来创建对象。但是在一个工厂里存在多种产品,会导致工厂的频繁修改工厂方法:实现了不同的产品存在于不同的工厂,使其解耦抽象工厂:实现了不同的工厂里面不止一种产品 简单......