【操作系统】段描述符、全局描述符表和选择子

一、保护模式的内存寻址过程

        与实模式不同的是,保护模式下内存段不再是简单地用段寄存器加载一下段基址然后乘以16位结合偏移地址得出实际要访问的内存地址,而是通过选择子全局描述符表中找到对应的段描述符,CPU从段描述符中提取段基址,再与偏移地址结合得出实际要访问的内存地址。

        以下我们来模拟一下保护模式寻址过程,下面会出现大量的专业术语,暂时看不懂也没关系,后面会有各个名词的详解:

        首先,在保护模式下,每一个内存段都不能再被简单地直接读取,而是用一个表来记录每一个内存段的详细信息(为了安全),该表包括这段内存谁可以访问(权限),起始地址在哪里(段基址)等等。每一段内存段都会有一个表格来记录,这个用来描述某一个内存段的表就叫做段描述符

         段描述符可不是随随便便乱扔的,系统会将这些段描述符全部收集整理在一起,并且汇总成一个list目录,这个list目录就称之为描述符表。按照描述符的作用域,又可以将描述符表分为全局描述符表(GDT局部描述符表(LDT

         由于段基址存放在了段描述符中,所以那些段寄存器就不再存放段基址了,而是存放一个称之为选择子(selector的东西。选择子是一个索引值,此索引值用于在段描述符中索引相应的段描述符,从段描述符中得到内存段的起始地址和段界限等相关信息。

        至此,所有主角已经全部登场,我们可以从头到尾推理一遍保护模式的寻址过程。首先,保护模式下的寻址方式为“段基址:段内偏移地址”。在访问具体某个内存段时,根据段寄存器中的选择子索引到全局描述符中该内存段的段描述符,并从该段描述符中提取出需要访问的内存段的段基址部分,最后CPU将段基址与段内偏移地址相加,得到实际的访存地址。

         由此也可以得出实模式下与保护模式下的区别:

功能

实模式

保护模式

访问内存的方式

段基址:段内偏移地址

段寄存器保存的内容

段基址

选择子

获取内存地址的方式

段基址乘以16,再与段内偏移地址相加得出实际内存地址

选择子索引到描述符后,CPU自动从段描述符中取出段基址,再加上段内偏移地址得出内存地址

二、选择子

        我们重新将选择子的结构图示端上来:

         可以看到选择子是16位的(因为段寄存器也是16位的),其中0~2位是RPL位,用于存储请求者的当前权限级别,权限级别有0、1、2、3四个等级。2~3位为TI位(Table indicator),用来表明选择子是在全局描述符表(GDT)中,还是在局部局部描述符表(LDT)中索引描述符,剩下的3~15位就是描述符的索引值,用于在描述符表中索引对应的描述符,选择子的索引值一共13位,即2的13次方8192,所以选择子最多可以索引8192个段,这与全局描述符表最多能容纳8192个描述符是吻合的。

三、段描述符

        段描述符用于描述一段内存段的属性信息,该结构一共8个字节,结构如图先所示:

         PS:可以看到,结构里面“段基址”和“段界限”两个属性被人为拆分放置在各个位置上,这其实属于一个历史遗留的问题,具体的原因不在此赘述,你可以理解为Intel为了兼容旧时代的CPU而做的兼容性措施,实际上使用时CPU会将其合并在一起形成一个连续完整的信息,并且存放在段描述符缓冲寄存器(如果不知道什么时段描述符缓冲寄存器,可以查看以前写的一篇文章:XXX)中以备后续使用。

         下面我们在将每个位置都进行讲解:

(1)段基址位:这个没有什么好说的,将低32位的16~31位、高32位的0~7位以及高32位的24~31位组合在一起就是32位的段基址了

(2)G字段:Granularity粒度,处于高32位的23位中,用来指定段界限的单位大小的,如果G位为0,则代表段界限的单位为1字节,如果G位为1,则代表段界限的单位为4KB。

(3)段界限:位于低32位的0~15位,以及高32位的16~19位,凑成20位的段界限属性,该属性值用于限制段内偏移地址的,段内偏移地址必须在段界限范围以内,否则CPU将会认为是非法访问,并且抛出异常。段界限有最大界限最小界限两种。对于数据段和代码段这种属于向上扩充的,地址越来越高的,此时段界限用来表示段内偏移地址的最大范围,而栈段则是向下扩充的,地址越来越低,此时段界限用来表示段内偏移地址的最小范围。那段界限可以表示的最大的范围是多少呢?这个主要是按照G字段的值来确定的:

  •          如果G位为0,那个段界限的单位为1字节,那么段的范围最多可以表示为2的20次方等于1MB
  •         如果G位为1,那个段界限的单位为4KB字节,那么段的范围最多可以表示为2的32次方(4KB等于2的12次方,12+20=32)等于4GB 

(4)DPL字段:位于高32位的13~14位,Descriptor Privilege Level描述符特权级,用于表示内存段的特权级,特权级分为0~3级,数字越小权限越大,用户一般在3级,而CPU一般在0级。

(5)P字段:位于高32位的15位,Present在位符,如果该值为1,则表示段存在内存中,否则为0,该字段由CPU自行检查,我们来进行赋值。该字段其实也是为了兼容新的CPU做的措施,旧时代的CPU因为内存不足,允许段描述符中对应的内存段暂时换出来存放到硬盘中,待需要使用时再加载进行,现在的CPU即使内存不足也不会将整段换出,而是通过分页的功能按照页的单位将内存换入换出。

(6)AVL字段:位于高32位的20位,该字段没有专门的用途,我们可以随意的操作此位

(7)L字段:位于高32位的21位,用于设置是否位64位代码段,1即为64位代码段,0即为32位代码段。

(8)D/B字段:位于高32位的22位,用来指定有效地址(段内偏移地址)以及操作数的大小,指定操作数的大小,也就是对“指令”来说的,和指令相关的内存段是代码段和栈段,所以该字段会根据代码段/栈段的不同定义也不同。

  • 如果是代码段,此位是D位,如果为0,表示指令中的有效地址和操作数是16位,指令有效地址用IP寄存器。如果为1,表示指令中的有效地址和操作数是32位的,指令有效地址用EIP寄存器。
  • 如果是栈段,此位是B位,用来指定操作数的大小,如果为0,使用的是sp寄存器,那么栈的起始地址是16位寄存器的最大寻址范围0xFFFF。如果为1,使用的是esp寄存器,那么栈的起始地址是32位寄存器的最大寻址范围0xFFFFFFFF。

(9)S字段:位于高32位的12位,该字段和下面的type字段需要配合着用,该字段用于描述当前描述符是否为系统段,0表示系统段,1表示数据段/非系统段。系统段是各种称呼为“门”的结构,比如调用们、任务门之类的,它是硬件系统需要的结构,我们目前只需要关注非系统段(S字段为1)的就行。

(10)Type字段:位于高32位的8~11位,是整个段描述符中比较重要的字段,用来指定本段描述符的类型的,类型由以下这些:

        我们目前只用到非系统段,所以当前只介绍非系统段的属性值。

  • A位表示Accessed,由CPU自行设置,每当该段被CPU访问过后,CPU会将此位设置成1,我们在调试时就可以根据此位判断该描述符是否可用,在创建一个新的段描述符时,需要将此位置设置为0。
  • C位表示Conforming,指如果自己是转移的目标段,并且自己是一致性代码段,那么自己的特权级一定要高于当前特权级,转移后的特权级不予自己的DPL为主,而是与转移前的低特权级一直,也就是听从转移前的低特权级。C为1时表示该段是一致性代码段,0时表示为非一致性代码段。
  • R位表示Read,指是否可读,1表示可读,0表示不可读。一般用来限制代码段的访问。比如指令执行过程中,CPU发现某些指令对R为0的段进行访问,CPU将会抛出异常。
  • X位表示Executable,指该段是否可以执行,X为1是代表代码段是可执行的,X为0时表示数据段是不可执行的。
  • E位表示Extend,用来标识段的扩展方向,E为0表示向上扩展,地址越来越高,用于代码段和数据段,E为1表示向下扩展,地址越来越低,用于栈段。
  • W位表示Writable,用来表明段是否可写,W为1表示可写,通常用于数据端,W为0表示不可写入,通常用于代码段。如果W为0的段有写入行为,CPU同样会抛出异常。

四、全局描述符表

        描述符表可以分为全局描述符表(GDT,golbal descriptor table)和局部描述符表(LDT,local descriptor table)。

        (1)全局描述符表

        全局描述符表相当于描述符的数组,数组中每个元素都是8字节的描述符,可以用选择子中提供的下标在GDT中索引描述符。全局描述符表的“全局”体现在多个成都在里面定义自己的段描述符,是公用的。

        (2)局部描述符表

        局部描述符表和全局描述表其实差不多都是一个容纳描述符的数组,区别CPU厂商建议每个任务的私有内存段都使用自己的段描述符表来装,这个就是局部描述符表,随着任务之间的切换,局部描述符表也跟着切换,但是现实中很少用到局部描述符表,所以这里点到为止。

        (3)描述符表寄存器

        描述符表一般都位于内存中,需要使用专门的寄存器来初始化、调用它,用在全局描述符表的寄存器叫做GDTR,也就是GDT Register,用在局部描述符表的寄存器叫做LDTR,也就是LDT Register,寄存器的结构如下:

        在使用描述符表寄存器之前,需要对寄存器进行初始化操作,指令格式为:

         Lgdt  48位内存数据

         48位内存数据刚好对应寄存器的48位,其中前16位为GDT的以字节为单位的界限值,后32为是GDT的起始地址。

        

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Fecify站点斗篷cloak

斗篷cloak站点斗篷模式功能发布!全新的应用场景,该模式是针对推广不用GMC,而是通过facebook,或者其他的一些平台/工具推广,这些推广方式的特点是:不需要商品的图片,或者说不会排查商品图片的侵权…

【DM8】分区表维护

查询分区 数据字典:dba_tab_pattitions SELECT * FROM SYS.DBA_TAB_PARTITIONS WHERE TABLE_OWNERTEST;添加分区 ALTER TABLE TEST.T1 ADD PARTITION Pn VALUES LESS THAN(MAXVALUE);删除分区 ALTER TABLE TEST.T1 DROP PARTITION Pn;合并分区 ALTER TABLE TES…

【算法】二分算法题

个人主页 : zxctscl 如有转载请先通知 题目 1. 704. 二分查找1.1 分析1.2 代码 2. 34. 在排序数组中查找元素的第一个和最后一个位置2.1 分析2.2 代码 3. 35. 搜索插入位置3.1 分析3.2 代码 4. 852. 山脉数组的峰顶索引4.1 分析4.2 代码 5. 153. 寻找旋转排序数组中…

TiDB 实战分享丨第三方支付企业的核心数据库升级之路

本文介绍了一家第三方支付企业在面对市场竞争和监管压力的态势下,通过升级核心数据库来提升业务能力的实践。该企业选择 TiDB 分布式数据库,成功将其应用于核心业务、计费、清结算和交易查询等关键系统。TiDB 的水平扩展能力、高可用性和简化数据栈等优势…

异常的种类

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 Oracle 运行时错误可以分为 Oracle 错误和用户自定义错误,与此对应,根据异常产生的机制和原理,可将 Oracle 的系统异常分为 3 种 预定义…

MySQL事务以及并发访问隔离级别

MySQL事务以及并发问题 事务1.什么是事务2.MySQL如何开启事务3.事务提交方式4.事务原理5.事务的四大特性(ACID) 事务并发问题1.并发引起的三个问题2.事务隔离级别 事务 在 MySQL 中,事务支持是在引擎层实现的。MySQL 是一个支持多引擎的系统&…

猫头虎博主分享运维技巧: 解决RuntimeError: Expected all tensors to be on the same device

博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …

Java基于SpringBoot+Vue的专家医院预约挂号系统,附源码

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

【C++航海王:追寻罗杰的编程之路】探寻实用的调试技巧

目录 1 -> 什么是bug? 2 -> 调试是什么?有多重要? 2.1 -> 调试是什么? 2.2 -> 调试的基本步骤 2.3 -> Debug和Release的介绍 3 -> Windows环境调试介绍 3.1 -> 调试环境的准备 3.2 -> 学会快捷键…

7-17 爬动的蠕虫

题目链接&#xff1a;7-17 爬动的蠕虫 一. 题目 1. 题目 2. 输入输出样例 3. 限制 二、代码 1. 代码实现 #include <stdio.h>int main(void) {unsigned int n, u, d;unsigned int minute, high;if (scanf("%d %d %d", &n, &u, &d) ! 3) {retur…

【CKA模拟题】边车容器Shared-Volume的具体用法

Useful Resources: Persistent Volumes Claim , Pod to Use a PV 题干 For this question, please set this context (In exam, diff cluster name) kubectl config use-context kubernetes-adminkubernetes An existing nginx pod, my-pod-cka and Persistent Volume Claim…

GIF在线生成器

上传图片就能生成GIF的前端WEB工具 源码也非常简单 <!DOCTYPE html> <html lang"zh" class"dark"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1, m…

设计模式学习笔记 - 设计模式与范式 -行为型:8.状态模式:游戏、工作流引擎中常用的状态机是如何实现的?

概述 本章学习状态模式。在实际的开发中&#xff0c;状态模式并不是很常用&#xff0c;但是在能够用到的场景里&#xff0c;它可以发挥很大的作用。从这一点上看&#xff0c;它有点像我们之前讲到的组合模式。 状态模式一般用来实现状态机&#xff0c;而状态机常用在游戏、工…

前端三剑客 —— JavaScript (第一节)

目录 回顾内容 1.弹性布局 2.网格布局 JavaScript 概述 发展 浏览器 什么是Javascript JavaScript 能干什么 JavaScript需要的环境 JavaScript初体验 基本数据 JS书写方式 行内JS 页面JS 外部JS 1&#xff09;创建外部JS文件 2&#xff09;编写页面 对话框 警…

类的函数成员(四):赋值函数

一.赋值函数是什么&#xff1f; 1.1 运算符的重载 运算符的重载实际是一种特殊的函数重载&#xff0c;必须定义一个函数&#xff0c;并告诉C编译器&#xff0c;当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数&#xff0c;它通常为类的成员函数。 定义运算符重…

TCP 重传、滑动窗口、流量控制、拥塞控制(计算机网络)

重传机制 TCP 针对数据包丢失的情况&#xff0c;会用重传机制解决。 接下来说说常见的重传机制&#xff1a; 超时重传快速重传SACKD-SACK 超时重传 重传机制的其中一个方式&#xff0c;就是在发送数据时&#xff0c;设定一个定时器&#xff0c;当超过指定的时间后&#xff0c…

HarmonyOS实战开发-如何使用 geolocation 实现获取当前位置经纬度

介绍 本示例使用 geolocation 实现获取当前位置的经纬度,然后通过 http 将经纬度作为请求参数,获取到该经纬度所在的城市。通过 AlphabetIndexer 容器组件实现按逻辑结构快速定位容器显示区域。 效果预览 使用说明 1.进入主页,点击国内热门城市,配送地址会更新为选择的城…

javaScript 事件循环 Event Loop

一、前言 javaScript 是单线程的编程语言&#xff0c;只能同一时间内做一件事情&#xff0c;按照顺序来处理事件&#xff0c;但是在遇到异步事件的时候&#xff0c;js线程并没有阻塞&#xff0c;还会继续执行&#xff0c;这又是为什么呢&#xff1f; 二、基础知识 堆&#x…

vivado HDL 例化调试探测流程概述

HDL 例化调试探测流程概述 HDL 例化探测流程涉及在 HDL 设计源代码中直接手动自定义、例化和连接各种调试核组件。下表中显示了 Vivado 工具中此流程所支持的新调试核。 新的 ILA 核相比于传统 ILA v1.x 核具有以下 2 大优势 &#xff1a; • 可搭配集成的 Vivado Log…

FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习

FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习 临界段代码保护 所谓临界段代码保护就是指必须完成运行&#xff0c;不能被打断的代码段。比如需要严格按照时序除初始化的外设&#xff1a;IIC、SPI&#xff0c;再或者因为系统自身需求和用户需求。 FreeRTOS 在进入临界…