特权级那些事儿-实模式下分段机制首次出现的原因

news2024/10/2 1:39:24

前言:

  操作系统的特权级模块在整个操作系统的学习中应该算的上是最难啃的了,提到特权级就要绕不开保护模式下的分段机制;如果想要彻底弄明白就要对比实模式下的分段机制有什么缺陷。这就衍生出很多问题如:什么是实模式?采用分段机制进行寻址的意义?什么是保护模式?为什么要设计保护模式?…?
  如果只是针对性的单独回答某个问题一直在罗列八股文要点,只讲“是什么”而忽略“为什么”,初学者看完之后会感觉始终没学透彻,更适合与有基础的人复习。任何事物发展到今天,都有段“合理”的过程,了解这个过程是怎么来的,有助于理解它今天的形态。在“处理器的发展史”中分段机制、保护模式等每一个机制的出现都是为了解决当时的一个重大问题,要想彻底把特权级模块啃透,就必不可免要去了解一下“处理器的发展史”(之所以是“处理器的发展史”而不是“操作系统的发展史”,是因为分段机制、保护模式、特权级这些概念都是处理器的工程师们设计的,操作系统只是一个协助者和应用者,后面会解释)。很多文章在梳理“处理器的发展史”的过程中无法抓住重点,花费大量篇幅讲解不关键的点,让人读起来容易分散精力,甚至读完以后忘记为啥点进来了。比如:为什么要采用分段机制进行寻址?“从分段机制首次出现的8086 处理器开始讲起,花费大量篇幅介绍8086 处理器是如何利用分段机制 在16位寄存器的基础上使用20位地址总线进行寻址的。” 却忽略了分段机制引入的关键是为了解决程序动态重定向问题,或者一笔带过。在计算机的发展史中每一种新机制的引入都是为了解决一个或多个痛点,我们要抓住最痛的那个点,因为不到万不得已是不会引入新机制的,充分利用20位地址总线的问题显然增加寄存器的容量为20位来的更简单,并不是引入分段机制的核心原因。文章开始前给大家推荐三本书:操作系统真相还原(强烈推荐)、30天自制操作系统、深入理解Linux内核
  接下来我们就沿着“处理器的发展史”来看下操作系统的特权级机制是如何发展的。首先我们挑选两个里程碑:1.分段机制首次出现;2.保护模式首次出现
  在看 CPU 的实模式和保护模式的文章之前,建议大家先花费十几分钟时间,看下这几篇处理器相关文章:因为无论 CPU 在哪种模式下工作,核心工作原理是不变的,有了这一思想武装起来后再讲模式就简单多了。
CPU与指令集、自研指令集难吗?、CPU如何执行指令以及流水线技术


  本篇主要是围绕第一个里程碑来讲解“实模式”下的分段机制。首选介绍一下什么是“实模式”,CPU 中本来是没有实模式这一称呼的,是因为有了保护模式后,为了与老的模式区别开来,所以称老的模式为实模式。就好比汽车中的自动挡和手动挡,本来是没有手动挡这个概念的,因为后来有了自动挡汽车,先前的汽车就被称为手动挡汽车了。

  在“处理器的发展史” 分段机制首次出现在Intel的8086CPU中。不知道大家有没有这样的疑虑这都2023年了还用8086这样的古董CPU讲解知识是否太落伍了,学到的知识在现代CPU中能用得上嘛。这个请大家放心,Intel 8086 CPU 应该称为80x86,它是Intel x86指令集架构下的第一款CPU,其中的86代表的是x86指令集体系,自那以后的CPU称为 286 386 486 586……即使现在的”酷睿“、”奔腾“也都属于x86系列。所以道理是不变的,而且用最简单的 80x86 CPU 学习,更容易理解和看透 CPU 运行机制。

  8086之所以称为CPU界的里程碑就是因为它引入了分段机制来进行内存访问。在它之前的CPU对内存的访问比8086还有“实诚”,它们没有段的概念,程序要计算机上运行必须采用静态重定位的方式将内存地址“硬编码“写死在代码中,这导致程序首次装入内存后程序在内存中的位置就不可移动了,操作系统无法再次对程序进行重定位操作。

  什么是静态重定向?我们用高级语言编写的源代码想要机器上运行需经历 编辑、编译、链接、装入、运行五个阶段,要想在CPU上运行就要遵循CPU的规则,在8086之前的CPU没有”段“的概念,CPU直接以真实物理地址访问内存,没有任何花哨,要在CPU上运行的程序必须直接使用包含真实物理地址的指令。首先编译和链接阶段由于编译器和链接器不能确定程序装入内存后的真实物理地址,所以产生的机器指令都采用以“0x00000”作为参考地址的相对地址也称为逻辑地址,等待程序首次装入内存时操作系统的装入程序模块会对目标程序中的地址相关的机器指令进行修改,将逻辑地址转换为真实的物理地址,这个过程就是静态重定向。列入:程序装入前机器指令 mov eax,0x00100 ,代表将逻辑地址0x00100中的数据"666"装入寄存器eax,程序装入时操作系统分配了以0x0c000为起始位置的物理内存,此时"666"对应的真实物理地址为0x0c100,因此上述指令会被修改为 mov eax,0x0c100 ,这样程序就能准确无误的在CPU上运行了但此后程序内存中的位置就不可移动了,因为指令中的物理地址已经不会再被改变了,如果程序移动了就会出现不可知异常。

在这里插入图片描述

  明白了静态重定向,我们来看下仅仅依靠静态重定向的程序在运行时有什么弊端。在静态重定向机制下程序首次装入内存后就不能再次在内存中移动了,在这个前提下再牛逼的操作系统也无法高效的完成内存的回收和分配。导致本就不富裕的内存空间还会产生大量的外部碎片,无法被充分利用。如下场景,有可用内存160KB,但由于程序A位置不可移动,程序B虽然仅需100KB内存却无法运行。
在这里插入图片描述
  上述囧况让本不富裕的内存雪上加霜,Intel早期的工程师难以承受内心的自责,不顾自己满头白发,熬了无数个通宵后,终于发明了“段”,从此CPU访问内存采用“段+偏移”的形式,地址重定位由静态重定位变为动态重定位。

  怎么理解动态重定位?动态重定位不需要在装入阶段进行指令的修改,而是在执行阶段每次CPU访问内存时进行逻辑地址物理地址的转换,动态重定位不需要装入程序模块的软件支持,需要CPU的硬件支持。引入段基址寄存器,用于保存程序被分配的起始物理地址段基地址,编译和链接后生成的逻辑地址段内偏移地址。程序运行过程中CPU在访问内存时会将段基址寄存器中的段基地址段内偏移地址相加得到真正的物理地址
在这里插入图片描述
如上图程序在装入内存后地址相关的机器指令不会被修改,依旧使用编译链接后的逻辑地址逻辑地址物理地址的转换是在执行包含内存寻址相关指令时由硬件完成的。转换的方式就是取段基址寄存器中的段基地址逻辑地址相加的结果进行寻址。如若操作系统想要移动程序在内存中的位置只要将段基址寄存器中的段基地址更新为新位置的起始物理地址即可。

  在分段机制下程序可以在内存中随意移动,这给了操作系统在内存管理方面很大发挥空间。例如:上述内存充足,但碎片化导致程序B无法运行的囧况就可以通过内存压缩的方法将内存碎片合并。
在这里插入图片描述

  处理器工程师还在分段机制的基础上将每个运行时程序所占内存分解成了:代码段+数据段+栈段+BSS段+堆段,将连续逻辑地址空间分散到多个非连续的物理内存空间中,这也是内存分配的一种优化,可以合理利用内存碎片如下图。


处理器工程师还为每个内存段设计了专门的段基址寄存器用来储存段基址如:cs、ds、ss、es、fs、gs。

  • 代码段:代码段简而言之就是把所有指令都连续排放在一起,形成了一个全部都是指令的区域,里面储存的是指令的操作码及寻址方式等。该区域可以在硬盘上的文件中,也可以是被加载后的内存中,总之是一段指令区域。它们内部都是紧凑挨着的,内容形式完全一样,只是存放的介质不一样而已。CPU专门提供了CS段寄存器用来保存指向这个块区域的起始地址。CS寄存器中的起始地址加上IP寄存器中的段内偏移地址为CPU提供了导航功能。CPU执行到何处完全要听这两个寄存器的安排。
  • 数据段:数据段和代码段类似,只是这段区域中的内容不是指令,而是存粹的数据,也就是说里面存储的是程序运行所需要的数据,属于指令中的操作数。CPU专门提供了DS段寄存器用来储存这段区域的起始地址。
  • 栈段:栈段是在内存中,硬盘文件中没有,一般的栈段是由操作系统分配指定的,所以是程序被加载到内存后才有的,栈段算是一种特别的数据段,和普通数据段不同的是栈段起始地址是高地址,向低地址方向扩展。所以CPU专门提供了SS段寄存器用来储存这块区域的起始地址。

  代码段(cs)、数据段(ds)、栈段(ss)寄存器从名字上就比较容易理解,那es、fs、gs这三个附加段寄存器是干吗的?其实就是多给大家提供几个段寄存器用而已,可以作为BSS段和堆段的段基址寄存器,多几个寄存器用不是更好嘛,省的紧紧巴巴的,纯粹是为了方便大家。

  以上就是分段策略出现的原因,它就是首次在8086上出现,自那之后的CPU都是用这类思想访问内存,只是在形式上有小改动,所以8086如此极负盛名。随着技术的发展虽然后来设计出了分页机制,它可以更好的管理内存,提高内存的利用率和内存交互性能。但任何事物发展到今天,都有段“合理”的过程,虽然分段机制并不完美,但它依然是处理器发展史上的一个里程碑,由于x86架构的CPU都会向前兼容,所以分段机制时至今日依然被保留了下来,目前CPU大都采用分段+分页机制并存的段页式内存管理机制,CPU在进行内存寻址前先将逻辑地址通过分段机制转换成虚拟地址(线性地址),虚拟地址再经过分页机制转换成真实的物理地址,当然也可以和 Linux 操作系统那样让每一段的基地址都设为 0 ,这样就等于“绕开”了段机制。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos 注册中心核心能力以及现实原理解析

Nacos注册中心主要分两方面解析:动态服务发现和Nacos实现动态服务发现的原理; 动态服务发现 服务发现是指使用一个注册中心来记录分布式系统中的全部服务的信息,以便其他服务能够快速的找到这些已注册的服务。 在单体应用中,DNS…

MINE: Towards Continuous Depth MPI with NeRF for Novel View Synthesis

MINE: Towards Continuous Depth MPI with NeRF for Novel View Synthesis:利用NeRF实现新视图合成的连续深度MPI 摘要:在论文中,提出了MINE,通过从单个图像进行密集3D重建来执行新的视图合成和深度估计。通过引入神经辐射场&…

05-Oracle中的对象(视图,索引,同义词,系列)

本章主要内容: 1.视图管理:视图新增,修改,删除; 2.索引管理:索引目的,创建,修改,删除; 3.同义词管理:同义词的作用,创建&#xff0…

如何通过websoket实现即时通讯+断线重连?

本篇博客只是一个demo,具体应用还要结合项目实际情况,以下是目录结构: 1.首先通过express搭建一个本地服务器 npm install express 2.在serve.js中自定义测试数据 const express require(express); const app express(); const http req…

详细stm32驱动SDRAM的注意事项以及在keil中的使用

SDRAM的主要参数: 容量:SDRAM的容量是指其可以存储的数据量,通常以兆字节(MB)或千兆字节(GB)为单位。 时钟频率:SDRAM的时钟频率指的是其内部时钟的速度,通常以兆赫&…

94. 二叉树的中序遍历

94. 二叉树的中序遍历 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 (左根右)。 首先我们需要了解什么是二叉树的中序遍历:按照访问左子树——根节点——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历…

MQTT协议-订阅主题和订阅确认

MQTT协议-订阅主题和订阅确认 SUBSCRIBE——订阅主题 订阅是客户端向服务端订阅 订阅报文 订阅报文与CONNECT报文类似,都是由固定报头可变报头有效载荷组成 固定报头比较简单,也是由两个字节组成,第一个字节为82,第二个字节是…

像素密度提升33%,Quest Pro动态注视点渲染原理详解

在Connect 2022上,Meta发布了Quest Pro,并首次在VR中引入动态注视点渲染(ETFR)功能,这是一种新型图形优化技术,特点是以用户注视点为中心,动态调节VR屏幕的清晰度(注视点中心最清晰、…

Oracle VM VirtualBox6.1.36导入ova虚拟机文件报错,代码: E_INVALIDARG (0x80070057)

问题 运维人员去客户现场部署应用服务,客户是windows server 服务器(客户不想买新机器),我们程序是在linux系统里运行(其实windows也可以,主要是为了保持各地环境一致方便更新和排查问题)我们使…

吐血整理学习方法,2年多功能测试成功进阶自动化测试,月薪23k+......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 测试进阶方向 测试进…

[Gin]框架底层实现理解(三)

1.engine.Run(port string) 这个就是gin框架的启动语句,看看就好了,下面我们解析一下那个engine.Handler() listenandserve 用于启动http包进行监听,获取链接conn // ListenAndServe listens on the TCP network address addr and then ca…

【SOP 】配电网故障重构方法研究【IEEE33节点】(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Java中wait和sleep区别

文章目录1. Java中wait和sleep区别2. wait和sleep所属方法的不同3. wait的Demo3.1 没有synchronized同步代码块异常3.2 wait使用Demo4. sleep的Demo1. Java中wait和sleep区别 sleep属于Thread类中的static方法;wait属于Object类的方法sleep时线程状态进入TIMED_WAI…

java 如何实现在线日志

如何采集springboot日志至web页面查看 实现方案 基于Filter方式,在日志输出至控制台前,LoggerFitler 拦截日志通过websocket推送至前台页面 实现逻辑: LoggerFilter采集日志添加至LoggerQueue队列, LoggerConsumer 从LoggerQueue中采集推送至前台页面 #mermaid-s…

LearnOpenGL-光照-3.材质

本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject 文章目录材质例子1代码相关光照太强了例子2例子3: 不同的光源颜色材质 引出材质 如果我们想要在OpenGL中模拟…

TTS | 语音合成常见数据集及数据格式详情

linkLJSpeech网址 : The LJ Speech Dataset (keithito.com)数据集描述:数据集大小:2.6GB这是一个公共领域的语音数据集,由 13,100 个简短的音频剪辑组成 一位演讲者阅读 7 本非小说类书籍的段落。为每个剪辑提供转录。…

删除的文件怎么恢复?恢复方法在这里(支持Win和Mac)

案例:文件永久删除还能找回来吗?关于Win和Mac系统的恢复方法 “前几天我在清理电脑垃圾,不小心误删了重要的文件。有没有什么比较全面的方法,可以帮助我恢复删除的文件啊?在线急等回复!” 随着电脑使用的…

Golang 中 Slice的分析与使用(含源码)

文章目录1、slice结构体2、slice初始化3、append操作4、slice截取5、slice深拷贝6、值传递还是引用传递参考文献众所周知,在golang中,slice(切片)是我们最常使用到的一种数据结构,是一种可变长度的数组,本篇…

三维人脸实践:基于Face3D的渲染、生成与重构 <二>

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 3DMM方法,基于平均人脸模型,可广泛用于基于关键点的人脸生成、位姿检测以及渲染等,能够快速实现人脸建模与渲染。推…

MySQL基础篇3

第一章 多表关系实战 1.1 实战1:省和市 方案1:多张表,一对多 方案2:一张表,自关联一对多 id1 name‘北京’ p_id null; id2 name‘昌平’ p_id1 id3 name‘大兴’ p_id1 id3 name‘上海’ p_idnull id4 name‘浦东’…