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

news/2024/11/15 10:06:05/文章来源:https://www.cnblogs.com/lilpig/p/18353172

个人一直对硬件、操作系统等底层技术感兴趣,无奈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,此时系统中只有一个段,该段的大小就是全部的物理地址空间。这样可以简化内存的管理,告别地址转换。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/781279.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Datawhale X 魔搭 2024年AI夏令营第四期AIGC方向 Task01

从零入门AI生图原理&实践是 Datawhale 2024 年 AI 夏令营第四期的学习活动(“AIGC”方向),基于魔搭社区“可图Kolors-LoRA风格故事挑战赛”开展的实践学习——适合想 入门并实践 AIGC文生图、工作流搭建、LoRA微调 的学习者参与学习内容提要:从通过代码实现AI文生图逐渐…

搭建PostgreSQL高可用集群(基于Patroni+Etcd)

搭建PostgreSQL高可用集群(基于Patroni+Etcd) 1.主机环境准备节点名称 主机名 网卡 IP地址 OS 安装软件 角色作用PGSQL1 pgsql1 ens33 192.168.198.162 CentOS7 PostgreSQL、ETCD、Patroni 主数据库PGSQL2 pgsql2 ens33 192.168.198.163 CentOS7 PostgreSQL、ETCD、Patroni 备…

Pytorch入门:tensor张量的构建

tensor数据结构是pytorch的基础与核心,本文主要介绍三种常用的tensor张量的构建方式。 1.从已有其他数据转换为tensor数据 常用方法有如下两种:torch.tensor torch.Tensor上述两种方法有细微的差别,具体通过示例来进行展示运行结果为首先,torch.tensor会对转换前容器内元素的…

Centos7安装Java8

1.查看目前环境 rpm -qa|grep jdk原有系统安装有jdk,如果对于jdk有要求,我们就需要重新安装jdk 2.卸载原有jdk环境 rpm -e --nodeps 上面显示的东西这里,我们就需要一个一个去卸载 如果有感觉麻烦,可以使用如下命令 yum remove *openjdk* 3.重新检查java -versionrpm -qa|g…

VDI/VDE 2643 Part3 2008:10

VDI/VDE 2643 2008:10 第三部分[!NOTE] 原始PDF链接:https://www.doc88.com/p-50359701027029.htmlOptical 3D-measuring systems Multiple view systems based on area scanning Preliminary note The content of this guideline has been developed in strict accordance wi…

026.Vue3入门,父页面给子页面传递数据,在子页面不能修改,只能改自己的data内容

1、App.vue代码:<template><Father/> </template><script setup> import Father from ./view/Father.vue </script><style> </style>2、Father.vue代码:<template><h3>父页面</h3><Child :FatherMsg="m…

025.Vue3入门,父页面给子页面传递数据,校验Props给出默认值

1、App.vue代码:<template><Father/> </template><script setup> import Father from ./view/Father.vue </script><style> </style>2、Father.vue代码<template><h3>父页面</h3><Child :FMsg="msg"…

使用 Python 爬取豆瓣电影 Top250 多页数据

使用 Python 爬取豆瓣电影 Top250 多页数据 创建时间:2024-08-11 一、完整代码抓取单贞数据 中的评分 简介 评价人数 将上面的改为多页 https://movie.douban.com/top250?start=0import requests from lxml import etreeheader = {User-Agent: Mozilla/5.0 (Windows NT 1…

彼岸网壁纸抓取

彼岸网壁纸抓取 创建时间:2024-08-11 一、代码 1.1 代码 import os import random import timeimport requests from lxml import etreeurl = http://pic.netbian.com/ header = {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gec…

【笔记】【THM】Malware Analysis(恶意软件分析)(还在学)

二进制安全入坟【笔记】【THM】Malware Analysis(恶意软件分析) 探索恶意软件的世界,分析恶意软件如何感染系统并造成破坏。 恶意软件分析就像猫捉老鼠的游戏。恶意软件的作者一直在设计新的技术来躲避恶意软件分析师的眼睛,而恶意软件分析师也一直在寻找识别和抵消这些技术…

一次性能优化,单台4核8G机器支撑5万QPS

这篇文章的主题是记录一次Python程序的性能优化,在优化的过程中遇到的问题,以及如何去解决的。为大家提供一个优化的思路,首先要声明的一点是,我的方式不是唯一的,大家在性能优化之路上遇到的问题都绝对不止一个解决方案。 如何优化 首先大家要明确的一点是,脱离需求谈优…

豆瓣短评榜单短评下载

豆瓣短评榜单短评下载 创建时间:2024-08-07 一、完整代码 import requests from lxml import etreedef get_html(main_url):header = {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/12…