彻底搞懂CPU特权级

程序员在用户程序开发过程中,会遇到两个基本概念即用户态和内核态,我们所说的模式切换,就是用户态和内核态之间的切换。

用户态和内核态其实是CPU的特权级,所以模式的切换就是CPU特权级的切换,模式等同于特权级,不同的模式表示CPU处于不同的特权级下,因此CPU特权级的切换不能局限于用户态到内核态之间,理论上CPU可以在任何特权级之间互相切换。

CPU特权级表示处理器访问计算机资源时,CPU所处的特权级即CPL,CPU处于不同的特权级,它能访问的计算机资源范围不同,计算机资源包括内存段(代码段,数据段,栈段),IO设备,核心数据结构。

特权级根据权限大小,分为4个等级即0,1,2,3,如下图

在这里插入图片描述

特权级

由上图所示,特权级越小,它对应的数值越大,用户程序通常被赋予3级特权,CPU访问它时,CPU的特权级CPL为0,1,2,3就可以了,当CPU需要访问更高特权级的计算机资源时,CPU就需要进行特权级切换,使得CPU处于更高的特权级下,内核被赋予0级特权,CPU如果需要访问内核时,CPU的特权级CPL必须为0,系统程序被赋予的特权级在内核和用户程序之间,系统程序指的是设备驱动程序,虚拟机等一些系统服务。

由上文所述,每个计算机资源都被贴上一个标签即特权极,我们称这种特权极为计算机资源特权极,贴上标签后,只有当CPU特权极CPL被切换为比这个计算机资源特权极更高或者同等特权极时,才能访问这个计算机资源,例如某个计算机资源的特权极是2,那么CPU访问这个资源时,CPU特权极CPL至少和2相同,即数值上不能大于它,CPU特权极CPL必须处于即2,1,0时才可以访问。

我们把计算机资源特权极根据资源类型不同分为内存段特权极,IO设备特权极,核心数据结构存储在内存中,操作系统把它存储在数据段中,不过有的核心数据结构被赋予单独的特权极,其它的核心数据结构的特权极同它所在的数据段的特权极相同,因此我们总结为:计算机资源的特权极分为内存特权极和IO设备特权极,内存特权极又分为内存段特权极和核心数据结构特权极。

本篇文章基于linux操作系统保护模式,Inter x86处理器架构,主要是为了理解CPU特权极和计算机资源特权级的原理,至于其他操作系统和处理器的特权极,思路上相似,实现细节上各有不同罢了。

为了减少篇幅,便于消化理解,我会拆分为三篇文章,分为上中下三个部分,分别阐述特权极的不同话题。

计算机资源特权级之-内存特权级(DPL)

CPU特权级(CPL)的切换过程

计算机资源特权级之-IO设备特权极(IPL)

计算机资源特权级之内存特权极
开发人员编写的用户程序通常会进行系统调用,而系统调用的代码和数据在内核,因此一个完整的应用程序不仅包括开发人员编写的代码和数据,也包括内核的代码和数据,如下图
在这里插入图片描述

一个完整的应用程序

由上图得知,一个程序无论是用户程序,系统程序,内核都至少包括3个部分即栈段,数据段,代码段。

代码段存储程序编译后的机器指令,数据段存储代码段中用到的数据,例如全局变量,常量,静态变量等,栈段存储着栈结构数据(STACK),在函数调用过程中会动态创建和销毁栈结构,栈结构存储着当前函数的参数,局部变量,返回地址等,栈结构初始大小为0,随着函数的调用动态扩大和缩小,但所有的栈结构空间加起来不能超过栈段的大小。

如果用户程序请求其它特权级下的计算机资源,势必会调用其他特权级下代码段,数据段,栈段,一旦调用了,操作系统就会把其他特权级下的代码段,数据段,栈段链接进来,因此广泛地说,一个完整的用户程序可能包括所有特权级下的代码段,数据段,栈段,因此每个用户程序下的每个特权级下有各自独立的代码段,数据段,栈段,例如对于栈来说,用户态下是用户栈,内核态是内核栈,系统程序是系统程序栈。

内核代码段,数据段,栈段在计算机启动的时候,由内核自己把它的内核代码段,数据段,栈段设置为特权级0,其它特权级下的代码段,数据段,栈段统统由操作系统从文件中加载到内存,然后在内存中分配固定大小的代码段,数据段,栈段,并给这些内存段设置不同的特权极,也就是说它们的特权级是由操作系统决定的,那么这些计算机资源的特权级存储在哪里呢?

特权级存储在一个叫做描述符的结构里,如下图所示。
在这里插入图片描述

描述符层次结构

如上图所示,每个描述符占用8个字节,描述符分为段描述符,门描述符,而门描述符又分为任务门描述符,调用门描述符,中断门描述符,陷阱门描述。

描述符又存储在哪里呢?答案是存储在描述符表中,而描述符表又存储在内存中的一个特殊的数据段中,每个描述符表可以存储8192个描述符,那么一个描述符表最多占用的空间就是8192 *8,等于65536个字节即64kb,描述符表分为三种,如下图:
在这里插入图片描述

描述符表层次结构

由上图所示,描述表分为三类即中断描述符表(IDT),全局描述符表(GDT),局部描述符表(LDT)。

GDT和LDT中可以存储段描述符,调用门描述符,任务门描述符,IDT中可以存储调用门描述符,任务门描述符,中断门描述符,陷阱门描述符,可见调用门描述符和任务门描述符可以存储在上图中的三种描述符表中,中断门描述符和陷阱门描述符只能存储在IDT中,段描述符只能存储在GDT和LDT中。

GDT和LDT唯一的区别就是:是否全局的,GDT是全局的,所有程序共享的,LDT是局部的,可能只属于某个程序内部,IDT也是全局的,通常操作系统分配的内存段例如代码段,数据段,栈段的段描述符都存储在GDT中,为了便于理解,后文不再区分GDT和LDT,统一按照GDT阐述。

那么我们如何定位一个描述符呢,我们已经知道了描述符存储在描述符表中,那么我们可以把描述符表看做一个数组,这个数组的元素是描述符,那么我们只需要知道数组的起始地址和数组的索引,就可以定位一个描述符,数组的起始地址即描述符表的起始地址已经由操作系统存储在专门的寄存器了,索引在哪里呢?对于IDT,它的索引是中断相量号,对于GDT来说,它的索引是选择子,选择子长度为16位,其中高13位为GDT数组的真正索性,低2位是一个叫RPL的东东,大致意思是请求特权极,点到为止,后面会详细介绍,第3位不再介绍,无关轻重。两张图可以解释描述符定位过程,如下图:
在这里插入图片描述

对于中断描述符表(IDT)定位描述符

由上图所示,对于中断描述符表(IDT),它的起始地址存储在IDTR寄存器中,中断向量号是一个无符号整数,因为描述符占用8个字节,所以我们只需要IDTR寄存器中的起始地址加上中断向量号*8就可以得出描述符在内存中的位置。
在这里插入图片描述

对于全局描述符表(GDT)定位描述符

由上图所示,对于全局描述符表,它的起始地址存储在GDTR寄存器中,选择子的高13位是真正的索引,因为每个描述符占用8个字节,所以我们只需要GDTR寄存器中的起始地址加上真正索引*8就可以得出描述符在内存中的位置。

好了,描述符和描述符表的分类,以及描述符的定位过程介绍完了,下面我们来介绍下描述符即段描述符和门描述符。

段描述符:

操作系统加载程序和数据后,会在内存中分配代码段,数据段,栈段,他们的大小是固定的,操作系统分配好3个内存段后,会建立3个段描述符即代码段描述符,数据段描述符,栈段描述符,段描述符的简化版格式如下:
在这里插入图片描述

段描述符简化版

由上图所示,为了便于理解,整理了一个段描述符的简化版本,我们逐个看起

段的起始地址:就是操作系统分配的内存段的起始地址。

段特权级DPL_SEG:操作系统根据程序类型来分配的,例如对于用户程序,操作系统为它赋予特权极3,而设备驱动程序,操作系统为它赋予特权极很可能就是1,这是本篇最重要的概念之一,它表示了CPU访问这个内存段时,CPU特权级(CPL)要高于或者等于它(DPL_SEG),假如DPL_SEG为2,那么CPU特权级(CPL)要想访问这个段,CPL必须高于或者等于2即数值上小于等于2,也就是说,0,1,2这三种CPU特权级(CPL)都可以访问它。

段权限:包括可读,可写,可执行,是不是一致性代码段,是否被CPU访问过等,总共占用了4位,通过任意的权限组合,可以区分出一个段是代码段还是数据段,例如代码段通常是可执行,可读,不可写,另外代码段根据是不是一致性分为:不一执行代码段和一致性代码段,大部分的代码段都是非一致性的,一致性的代码段是干啥的,先不管它,后面说到CPU特权级切换时再说。

段大小:就是一个段的大小,表示了一个段从起始地址到段结束地址之间的范围。

其它:其它的位置如E位,表示段是向上或者向下扩展,通过这个位可以区分一个段是数据段还是栈段,向下扩展表示是栈段,向上扩展表示是数据段。

好了,段描述符介绍完了,再来看看门描述符。

门描述符

门描述符分为任务门描述符,调用门描述符,中断门描述符,陷阱门描述的,门描述主要包括两部分,如下图为门描述的简化版本
在这里插入图片描述

第一部分是一个处理程序的地址,我们可以把处理程序理解为一个函数,第二部分是这个门描述符的特权极,我们叫它DPL_DOOR。

我们上文说过,内存特权极分为内存段特权极和核心数据结构特权极,门描述符就是核心数据结构,它有自己独立的特权级,我们称它为DPL_DOOR即门特权极。

这里可以简单总结下:内存特权级分为内存段特权级(DPL_SEG,它在段描述符中)和门特权级(DPL_DOOR,它在门描述符中),内存段特权级(DPL_SEG)表示CPU访问这个内存段时,CPU特权级(CPL)必须高于或者等于DPL_SEG,从数值的角度来看即CPL<=DPL_SEG,门特权级(DPL_DOOR)表示CPU访问门描述符时,CPU特权级(CPL)必须高于或者等于DPL_DOOR,从数值的角度来看即CPL<=DPL_DOOR,无论内存段还是门描述符都属于计算机资源,通常访问这些资源时,CPU特权级高于或者等于这些资源所需的特权级也是正常的,从这个角度理解就可以了。

门描述符的作用就是定位一段处理程序,对于调用门描述符,中断门描述符,陷阱门描述,它们定位这段处理程序是通过选择子和处理程序偏移量,我们知道一个选择子可以定位到一个段描述符,这个段描述符存储了内存段的起始地址,对于处理程序来说,它是存储在代码段中的,因此知道了选择子就可以知道一个代码段的起始地址,然后处理程序不总是在代码段的起始位置,它通常需要一个偏移量,因此代码段的起始地址加上这个偏移量就定位到了处理程序的起始地址,对于任务门描述符,没有偏移量,它的偏移量存储在其它地方,我们后续不会再阐述任务门,因此不再对它进行介绍。

那么段描述符和门描述符的区别是什么呢,我们大概已经理解了吧,段描述符描述一个内存段的起始地址,大小,段特权级(DPL_SE),权限等,内存段可以是代码段,数据段,栈段,这个内存段里面存储什么,并不关心,而门描述符则描述了一段具体的处理程序,这段具体的程序肯定在代码段中的指定位置(因此需要选择子和偏移量),并且已经确定它是实现什么功能了,例如一个中断门描述符可以指向缺页中断处理程序,这个缺页中断处理程序只负责缺页的相关逻辑。

关于内存特权级阐述到这里了,我们来总结下吧

总结

每个计算机资源例如本文阐述的内存段(数据段,代码段,栈段)和核心存储结构(门描述符)都被操作系统打上了特权级标签,我们称这些内存资源的特权级为DPL,在文章里内存段特权级我们叫做段特权级(DPL_SEG),门描述符的特权级我们叫做门特权级(DPL_DOOR),通常我们访问内存段或者门描述符时,CPU特权级(CPL)必须高于或者等于内存特权级DPL(DPL_SEG和DPL_DOOR),从数值的角度看CPL<=DPL(DPL_SEG和DPL_DOOR)。

我们开发的程序中的代码,数据被操作系统加载后,会在内存中创建内存段即代码段,数据段,栈段,然后针对每个段创建一个段描述符,存储在全局描述符表中(GDT),我们根据段描述符在GDT中的索引和段特权级DPL_SEG,创建选择子,选择子的高13位存储索引,低2位存储请求特权级(RPL),然后将3个选择子存储在内存中。

RPL为请求特权级,通常我们会通过请求一个选择子,然后根据选择子获取对应的描述符,然后根据描述符去访问内存段资源和门描述符,那么RPL就表示选择子是由哪个特权级下的代码请求的,可以是操作系统,也可以是用户程序,通常一个选择子的RPL等于这个选择子指向的描述符中的特权级(DPL(DPL_SEG和DPL_DOOR)),RPL可以被修改,可以是善意的修改,也可以是恶意的修改,就先讲到这里,RPL的其它阐述就放在特权级切换文章中阐述,这里不再深究。

当CPU要执行某段代码时,操作系统会将代码段选择子加载到CS寄存器中,然后CPU可以根据CS寄存器中的选择子查找GDT,找到段描述符,然后根据段描述符找到代码段的起始地址,然后就可以从起始地址处执行代码了,在代码的执行过程中,会用到全局变量数据,常量或者静态数据,那么操作系统会将该程序的数据段选择子加载到DS寄存器中,然后CPU可以根据DS寄存器中的选择子查找GDT,找到了段描述符,然后根据段描述符找到数据段的起始地址,再加上指令中操作数(数据的偏移量),定位到数据,随着函数或者处理程序的不断调用,会涉及到出栈和入栈,因此操作系统会将该程序的栈段选择子加载到SS寄存器中,然后CPU可以根据SS寄存器中的选择子查找GDT,找到了段描述符,然后根据段描述符找到栈段的起始地址,然后创建栈结构,栈结构是可以随着函数的调用嵌套的,也可以随着一个函数的返回而销毁,每个栈结构都有个指针即栈顶指针,栈顶指针存储在栈指针寄存器中(ESP),用于指向当前栈的栈顶,还有一个指针即栈底指针,用于表示一个栈结构的起始位置,它存储在栈底寄存器(EBP),对于一个栈结构来说EBP是固定不变的。

那么门描述符干啥用的呢,门描述符用于指向一段处理程序,这段处理程序实现某个具体功能,那么门描述符的具体用途是什么呢,我们可以先留个底,门描述符的用途就是实现系统调用,再广泛一点就是实现不同特权级下切换的。

好了,内存特权级介绍这里了,下篇文章将重点阐述不同特权级下的代码和数据如何切换,需要消化本篇文章的概念后,才能理解下一篇文章。

番外篇
段描述符是在操作系统加载程序时,创建内存段的同时,创建了段描述符和选择子,各类门描述符是操作系统启动的时候,自动创建的并且内置到GDT或者IDT中的。

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

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

相关文章

Cesium 问题:[Violation]‘requestAnimationFrame‘ handler took 58ms

文章目录 问题分析解决问题 Cesium 在引入页面后,控制台弹出提示信息: [Violation]requestAnimationFrame handler took 58ms分析 这个警告信息表明使用 requestAnimationFrame 方法时,其处理函数执行所需的时间超过了一定阈值,从而触发了警告。requestAnimationFrame 方…

Java+SpringBoot:制造企业质量管理的双引擎

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

mysql高可用架构设计

一、主从架构 主从架构一般如下所示 这里从节点一般设置成只读&#xff08;readonly&#xff09;模式。这样做&#xff0c;有以下几个考虑&#xff1a; 有时候一些运营类的查询语句会被放到备库上去查&#xff0c;设置为只读可以防止误操作&#xff1b; 防止切换逻辑有 bug&a…

Linux——进程池

Linux——进程池 池化技术进程池信道模拟任务进程退出一个bug 今天我们来学习一下管道的应用——进程池。如果有没看过上一篇管道的小伙伴可以点击这里&#xff1a; https://blog.csdn.net/qq_67693066/article/details/136371517 池化技术 我们首先要了解一下池化技术&#x…

StarRocks实战——特来电StarRocks应用实践

目录 一、为何引入StarRocks 二、主要应用场景 三、封装或扩展 四、集群监控预警 五、总结规划展望 5.1 使用经验分享 5.2 下一步计划 5.2.1 StarRocks集群自动安装 5.2.2 StarRocks集群高可用架构 原文大佬的这篇StarRocks应用实践有借鉴意义&#xff0c;这里摘抄下来…

Socket网络编程(三)——TCP快速入门

目录 概述TCP连接可靠性1. 三次握手过程2. 四次挥手过程3. 为什么挥手需要四次&#xff1f; 传输可靠性TCP核心APITCP传输初始化配置&建立连接客户端创建Socket建立连接服务端创建ServerSocket监听连接ServerSocket 和 Socket的关系 Socket基本数据类型传输客户端数据传输服…

AI芯片行业深度:发展现状、竞争格局、市场空间及相关公司深度梳理

从广义上讲只要能够运行人工智能算法的芯片都叫作AI芯片&#xff0c;但通常意义上的AI芯片指的是针对人工智能算法做了特殊加速设计的芯片。AI芯片也被称为AI加速器或计算卡&#xff0c;即专门用于处理人工智能应用中的大量计算任务的模块&#xff08;其他非计算任务仍由CPU负责…

ACwing :1221 四平方和 (二分)

*#include <iostream> #include <cstring> #include <algorithm>using namespace std; const int N 5e6 10; int n;struct sum{int s,c,d;bool operator < (const sum &T)const{ // 重载小于符号if(s ! T.s) return s < T.s;if(c ! T.c) …

day11_oop_fianl_satic_多态

今日内容 零、 复习昨日 一、final 二、static 三、多态 四、向上转型&向下转型 五、多态应用 零、 复习昨日 0 类封装步骤 属性私有private提供setget方法 1 继承关键词,继承的好处 extends减少代码重复为多态做准备 2 子类可以使用父类什么 非私有的属性和方法 3 方法重写…

总结:直径测量的发展历程!在线测径仪已成主要方式!

测量在生活、生产和科学探究中扮演着至关重要的角色。从古至今&#xff0c;人们对测量的探索从未停止。而外径作为一种基础的几何尺寸&#xff0c;其测量也经过了多代发展&#xff0c;直到至今被广泛应用到工业生产中的在线测径仪。本文就来介绍一下外径测量的发展历程&#xf…

【pyinstaller打包记录】Linux系统打包可执行文件后,onnxruntime报警告(Init provider bridge failed)

简介 PyInstaller 是一个用于将 Python 程序打包成可执行文件&#xff08;可执行程序&#xff09;的工具。它能够将 Python 代码和其相关的依赖项&#xff08;包括 Python 解释器、依赖的模块、库文件等&#xff09;打包成一个独立的可执行文件&#xff0c;方便在不同环境中运行…

【Sql Server】存储过程的创建和使用事务,常见运用场景,以及目前现状

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

浅析扩散模型与图像生成【应用篇】(五)——SDEdit

5. SDEdit: Guided Image Synthesis and Editing With Stochastic Differential Equations 该文提出一种基于SDE扩散模型的引导图像生成和编辑方法。通过使用者在原图上给出一些引导&#xff0c;比如在图像上涂鸦或者增加一个图块&#xff0c;甚至可以不给定原图&#xff0c;直…

如何从 WordPress 中的静态资源中删除查询字符串

今天有一个客户来问询&#xff0c;hostease主机创建的WordPress站点&#xff0c;在GTMetrix或Pingdom进行网站速度测试&#xff0c;看到有关查询字符串的警告。如果不想看到查询字符串的警告&#xff0c;要如何处理呢?我们测试&#xff0c;可以通过一些处理满足这个需求。我们…

三整数排序问题的解题逻辑

【题目描述】 输入3个整数&#xff0c;从小到大排序后输出。 【样例输入】 20 7 33 【样例输出】 7 20 33 【解析】 本题解法大概有3种&#xff1a; 1、穷举条件法。 此方法先判断a、b、c大小的所有可能&#xff0c;再根据各种可能性输出不同的排序。 思路是先判断a、…

C++17中的类模板参数推导

在C17之前&#xff0c;必须明确指出类模板的所有参数。自从C17起必须指明类模板参数的限制被放宽了。通过使用类模板参数推导(Class Template Argument Deduction(CTAD))&#xff0c;只要编译器能根据初始值推导出所有模板参数&#xff0c;那么就可以不指明参数。 C17中的类模板…

记录一次排查负载均衡不能创建的排查过程

故障现象&#xff0c;某云上&#xff0c;运维同事在创建负载均衡的时候&#xff0c;发现可以创建资源&#xff0c;但是创建完之后&#xff0c;不显示对应的负载均衡。 创建负载均衡时候&#xff0c;按f12发现console有如下报错 后来请后端网络同事排查日志发现&#xff0c;是后…

中科大计网学习记录笔记(十七):拥塞控制原理 | TCP 拥塞控制

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…

论文学习—Model-based Adversarial Meta-Reinforcement Learning

Model-based Adversarial Meta-Reinforcement Learning Abstract1. Introduction2. Related work3 Preliminaries基于模型的强化学习&#xff08;MBRL&#xff09;:区别和联系&#xff1a; 4 Model-based Adversarial Meta-Reinforcement Learning4.1 Formulation 4.2 Computin…

LeetCode 每日一题 Day 88 - 94

2673. 使二叉树所有路径值相等的最小代价 给你一个整数 n 表示一棵 满二叉树 里面节点的数目&#xff0c;节点编号从 1 到 n 。根节点编号为 1 &#xff0c;树中每个非叶子节点 i 都有两个孩子&#xff0c;分别是左孩子 2 * i 和右孩子 2 * i 1 。 树中每个节点都有一个值&a…