转自:http://blog.csdn.net/rosetta/article/details/8570681
Linux操作系统基础(四)保护模式内存管理(2)
转载请注明出处:http://blog.csdn.net/rosetta
本节主要讲:保护模式内存管理相关的物理地址空间,逻辑和线性地址空间,段选择符,段寄存器,段描述符。
物理地址空间
保护模式下,IA-32架构提供了一个4GBytes(2^32bytes)正常大小的物理寻址空间。处理器可以使用地址总线录址这些地址空间。这些地址空间是平坦的,范围从0到FFFFFFFFH,这些物理地址空间可以映射到读写内存、只读内存和I/O内存。以下描述内存映射机制的内容可以分为分段和分页。
逻辑和线性地址空间
在系统保护模式中,处理器需要经过两步完成到物理地址空间的转换:逻辑地址转换和线性地址空间分页。
即使最小程度使用分段,处理器地址空间中的每一个字节都需要通过逻辑地址访问。一个逻辑地址由16位段选择符和32位偏移地址组成,如图10所示。段选择符确定该字节位于哪个段,偏移地址确定字节相对于段基地址的段内位置。
处理器转换逻辑地址到线性地址。线性地址是处理器线性地址空间中的32位地址。和物理地址类似,线性地址空间也是平坦的(不分段),大小2^32bytes,范围从0到FFFFFFFFH。
线性地址空间包含所有的段和系统定义的系统表。
从逻辑地址转换到线性地址,处理器需要做如下操作:
1. 使用段选择符在GDT或LDT中定位段的段描述符,然后读取到处理器中。(这步仅当一个新的段选择符被段寄存器加载时才需要)。
2. 检查段描述符的访问权限和段的范围以确保段是可以接受的,并且确保偏移在段限长内。
3. 偏移值和段描述符表中的段基地址相加最终形成线性地址。
假如分页不启用,处理器直接把线性地址映射到物理地址(也就是说,线性地址可以直接送到处理器的总线上)。假如启用分页,第二步地址转换将使用,这一步将把线性地址转换为物理地址。
图10 逻辑地址到线性地址的转换
段选择符
段选择符为16位用来标识一个段,如图11所示。它不直接直向段,而是通过指向段描述符,段描述符再定义段的信息。一个段选择符包含以下元素:
索引:位3和位15-共13位可以在GDT或LDT中选择8192个段描述符表。处理器把索引值乘以8(一个段描述符的大小为8字节)再加上GDT或LDT中的基地址就可以定位一个描述符的位置,GDT和LDT分别存放在GDTR和LDTR寄存器中。
TI(tableindicator)flag:位2-用来指定使用的是哪个表:TI=0,表示选择GDT;TI=1表示选择LDT。
Requested Privilege Level(RPL):位0和位1-指定选择符的特权级。特权级从0到3,0的特权限最高。
GDT表中的第一个描述符不能被处理器使用。指向GDT第一项(也就是TI=0,索引为0时)的段选择符叫做空段选择符。当一个段寄存器(如CS,SS)加载一个空选择符时处理器不会产生异常。但是当使用加载了空选择符的段寄存器访问内存时将会产生异常。空选择符可以用来初始化不使用的段寄存器。加载带有空选择符的段寄存器CS或SS将会产生一个一般保护性异常。
段选择符作为指针变量的一部分对应用层程序员是可见的,但是选择符的值通过由链接程序指定或者修改,而不是应用程序开发者。
图11 段选择符
段寄存器
为了减少转换时间和代码的复杂性,处理器提供6个可以存放段选择符的寄存器,如图12所示。每个段寄存器支持一个特定的内存引用(代码,数据和堆栈)。对于实际上任何程序执行,至少需要加载可用的段选择符到CS,DS和SS寄存器中。处理器还提供三个额外数据段寄存器:ES,FS和GS,它们可以给当前执行的任务(或程序)提供额外的可用数据段。
对于访问一个段的程序,这个段的段选择符必须加载到一个段寄存器中。所以,虽然一个系统可以定义成千的段,但只有6个段可被立即使用。其它段可以在程序执行过程中通过加载段对应的段选择符而使其生效。
图12 段寄存器
每个段寄存器都有可见部分和隐藏部分(隐藏部分也称“描述符缓冲”或“影子寄存器”)。当一个段选择符被加载到段寄存器的可见部分时,处理器也把由段选择符指向的段描述中的基地址,段限长和访问控制信息加载到段寄存器的隐藏部分。缓冲在段寄存器中(可见部分和隐藏部分)的信息使得处理器在进行地址转换时不需要花费额外的总线周期(周期指从段描述符中读取基地址和段限长的时间)。在一个多处理器系统中,处理器访问同一个描述符表,当描述符表被修改后,软件有职责重新加载到段寄存器。
以下两种方法提供加载段寄存器:
1,直接使用MOV,POP,LDS,LES,LSS,LGS和LFS 等指令加载。这些指令显示的引用段寄存器。
2,隐式的加载,比如使用长指针方法的CALL,JMP和RET指令,SYSENTER和SYSEXIT指令,IRET,INT n,INTO和INT3指令。这些指令执行的时候会伴随着CS寄存器内容的改变(有时其它寄存也会改变)。
MOV指令也可用来把段寄存器的可见部分存储到通用寄存器中。
段描述符
段描述符是GDT或LDT表中的一个数据结构项,它提供一个段的大小、位置、控制权限和状态信息。段描述符通常由编译器、链接器、加载器或操作系统创建,而不是应用程序创建。图13表示了所有类似的段描述符的通用格式。
图13 段描述符
段描述符中的标志表示的含义如下:
段限长(Segment Limint)
段限长定义了段的大小。物理器会把两个段限长位域连起来形成一个20位长的段限长值。处理器依据颗粒性标志G有不同值有两种解释段限长:
l 假如G=0,段限长范围1字节到1MByte,以字节为单位增长。
l 假如G=1,段限长范围从4KBytes到4GBytes,以4KByte为单位增加。
处理器使用段限长有两种不同的方式,依据type域中的段扩展方向标志E来决定是上扩段还是下扩段(E=0,为上扩段;E=1为下扩段,在“代码和数据段描述类型”一节会详细描述)。
对于上扩段,逻辑地址中的偏移值范围从0到段限长,当偏移值大于段限长时会产生通用保护异常;对于下扩段,段限长的功能刚好相反,根据下扩数据标志B(在描述D/B时会有具体解释),偏移值从段限长到FFFFFFFFH(4GB,B=1)或FFFFH(64KB, B=0),小于段限长的偏移值将会产生通用保护异常。对于下扩段,减少段限长值允许在段地址空间底部分配内存,而不是顶部。因为IA-32架构使用的栈总是向下增长的,所以这种机制方便于栈的扩展。
基地址(Base address)
确定一个段的0字节在4GByte线性地址空间中的位置。物理器会把三个基地址位域连起来形成一个32位长的段基地址值。段基地址应该16字节边界对齐。虽然16字节对齐不是必须的,但是这种对齐允许程序通过使数据段和代码段16字节边界对齐而达到最高性能
类型(type)
指明段或门的类型,定义段的访问类型和段的扩展方向。这个域根据描述符的类型有两种解释(数据代码段描述符和系统段描述符)。这个域的值会根据代码段、数据段还是系统段还不同。
S描述符类型(descriptortype)
指明段的类型是系统段(S=0)或数据段、代码段(S=1)。
DPL 描述符特权级(descriptorprivilege level)
指明段的特权级。特权限从0到3,0的特权级最高。DPL用来控制段的访问。
P 段存在标志(segment-present)
指明段是否在内存中(P=1,在内存;P=0,不在内存)。假如P=0,当一个段描述符的段选择符加载进段寄存器时处理器产生段不存在异常。内存管理软件可以使用此标志控制在某一给定时间内把哪个段加载进物理内存中,这为管理虚拟内存除分页以外的控制。如图14显示P=0时一个段描述符的格式。当这个标志被清除,操作系统可以*使用标为“可用”(Available)的位置来存储它自己的数据,例如不存在的段实际在什么地方的信息。
图14 当段存在位P=0时的段描述符
D/B(默认操作大小/默认栈指针大小/上界限)标示(defaultoperation size/default stack pointer size and/or upper bound)
根据段描述符是一个可执行代码段、下扩数据段、或堆栈段完成不同的功能。(在32位代码和数据段中必须设置为1;在16位数据和代码段中必须设置为0。)
l 可执行代码段。此时这个标志叫D标志,它指明段中的指令引用有效地址和操作数的默认长度。假如D=1,则默认是32位地址和32位或8位操作数;假如D=0,则默认是16位地址和16位或8 位操作数。指令前缀66H可用来选择非默值的操作数大小,指令67H可用来选择非默认值的操作地址大小。
l 堆栈段(数据段指针由SS寄存器指向)。此时这个标志叫B标志,它指明用于隐含栈操作(如pushes,pops,calls)时栈指针的大小。假如B=1,一个32位的栈指针被使用,它存储在32位ESP寄存器中;假如B=0,一个16位的栈指针被使用,它存储在16的SP寄存器中。假如堆栈段被设置成为下扩数据段,那么B标志也用来指定栈段的上界。
l 下扩段(Expand-downdata segment)。此时标志叫作B标志,它指明段的上界。假如B=1,上界为FFFFFFFFH(4Gbytes);B=0,上界为FFFFH(64KBytes)。
G颗粒性标志(granularity)
决定段限长域值(segment limit)的单位。当G=0时,段限长的单位为字节;当G=1时,段限长的单位为4-KByt(这个标志不会影响基地址的颗粒性,基地址的单位总是字节。)。
当G=1时使用段限长检查偏移值时,不会检查偏移值的低12位有效值。例如,当G=1时,段限长为0可以表示偏移值从0到4095。
可用和保留位(Available and reserved bits)
段描述符的第二个双字位20是给操作系统使用的;位21是64位代码段标志,在这里不作讨论,只把它设为0就行。
数据和代码段描述符类型
当段描述符中的S置位时,描述符用于代码或数据段描述符。类型(type)的最高有效位(第二个双字的位11)用于决定是数据段描述符(复位)还是代码段描述符(置位)。
对于数据段描述符,类型的低三个有效位(位8,9和10)解释为已访问(accessed A)、可写(write-enable W)和扩展方向(expansion-direction E),如图15所示。根据读取标志位(位9),决定数据段是只读还是可读写。
图15 数据和代码段类型
堆栈段必须是可读写数据段,如果SS寄存器加载一个不可写的数据段选择符将会产生通用保护异常。如果栈的大小需要动态改变,栈段段可以设置为下扩段(type域中的E标志为1)。在这里,动态改变栈段的段限长会使栈空间增加在栈的底部。如果栈段的大小需要保持静态,那么栈段可以是上扩段也可以是下扩段。
访问位指明自从操作系统复位些位以来该段是否被访问过。当段寄存器加载段的段选择符时处理器都使此位置位(假设包含此段描述符的内存类型是可写的)。此位一直保持直到需要被明确清除。此位可用来管理虚拟内存管理和调试。
对于代码段,类型(type)域的低三位从低到高依次解释为访问位(accessed A),可读位(read enable R),和一致性位(conforming C)。代码段依据可读位(R)的不同可分为仅执行或可读/可执行。当常量或其它静态数据以及代码指令放在ROM中时一个可读/可执行段可被使用。这里,代码段中的数据可被带CS前缀的指令或者数据段寄存器中(指DS,ES,FS和GS寄存器)的代码段的段选择符加载。在保护模式下,代码段永不能写。
代码段可以是一致性和非一致性的。允许执行在当前特权级时向更高权级的一致性代码段进行执行转移。当向不同特权级的非一致性代码段进行执行转移时,会产生一个通用保护异常,除非使用调用门(call gate)或任务门(task gate)(相关内容可看后面文章)。不访问受保护的和处理某些异常类型(如除出错,溢出)的系统功能可以放在一致性代码段。不能被更低特权级程序访问的需要被保护的程序功能应该放在非一致性代码段。
注意
不能通过call或jump转入更低优先级的代码段执行,不管目标段是一致性或非一致代码段。如果尝试这样转变将会引起一个通过保护异常。
所有的数据段都是非一致性的,这意味着它们不能被更低特权级的程序访问。不像代码段,数据段总是能被更高特权级的程序访问而不需要使用特别的访问门。
如果段描述符位于ROM中的GDT或LDT中,假如软件或处理器尝试往ROM中的段描述符更新(写入)时会进入无限循环。为了防止这种情况发生,把ROM中的所有段描述符的访问位都置位。并且,从操作系统代码中移除那些试图修改ROM中段描述符的代码。
参考:《Intel SystemProgramming Guide》
《Linux内核完全剖析》赵炯 编著
《新版汇编语言程序设计》钱晓捷 主编
Linux操作系统基础(四)保护模式内存管理(2)【转】的更多相关文章
-
操作系统学习(一)、80x86保护模式内存管理
整理的不好,凑合着看吧 目录 1.内存及寻址 2.地址变换 3.分段机制 4.分页机制 5.保护 6.去到底部 一.内存及寻址 返回目录 二.地址变换 80X86 从 逻辑地址 到 物理地址 的转换: ...
-
Linux操作系统-CentOS6启动流程和服务管理
Linux操作系统-CentOS6启动流程和服务管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Linux组成 1>.Linux: kernel+rootfs ker ...
-
Linux操作系统-CentOS7启动流程和服务管理
Linux操作系统-CentOS7启动流程和服务管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.systemd POST --> Boot Sequence --&g ...
-
十天学Linux内核之第三天---内存管理方式
原文:十天学Linux内核之第三天---内存管理方式 昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今 ...
-
第1课:Linux操作系统基础【DevOps基础培训】
第1课:Linux操作系统基础 --DevOps基础培训 1. 云主机.公网IP 1.1 公网ip和私网ip 只有公网ip是能够连接互联网的,私网IP 一般只用作局域网 我们能够上网靠的是isp组织分 ...
-
【CUDA 基础】4.2 内存管理
title: [CUDA 基础]4.2 内存管理 categories: - CUDA - Freshman tags: - CUDA内存管理 - CUDA内存分配和释放 - CUDA内存传输 - 固 ...
-
Linux 操作系统基础知识
1.操作系统总体介绍 •CPU: 就像人的大脑,主要负责相关事情的判断以及实际处理的机制.查询指令: cat /proc/cpuinfo•内存: 大脑中的记忆区块,将皮肤.眼睛等所收集到的信息记录起来 ...
-
Linux操作系统基础(完结)
摘要 一.Linux操作系统概述 二.Linux操作系统安装 三.Linux文件系统及文件基础 四.Linux操作系统命令使用基础 五.Linux应用程序的安装与卸载基础 五.用户及进程 六.相关信息 ...
-
(笔记)Linux内核学习(九)之内核内存管理方式
一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...
随机推荐
-
使用ETag进行session的降级
回顾 在web后台开发中我们经常需要存储一些变量到session中进行暂存,最为特殊的就是"购物车",由于http的无状态特性,因此我们需要在客户端打上一个标记,唯一的标示客户端并 ...
-
git如何使用 svn如何使用
git和svn是2款常用的版本控制系统. git 的功能: 1.从服务器上克隆完整的Git仓库(包括代码和版本信息)到单机上. 也就是说自己机器上有一个git仓库. 这和svn是不同的,svn是没有本 ...
-
sql server 修改表的默认值, 需要先删除约束条件
---------增加是否发布订单 if not exists(select 1 from syscolumns where name='iIsRelease' and id=OBJECT_ID('M ...
-
C# 通过url地址获取页面内容
using System.Net; using System.IO; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(" ...
-
接口测试基础(fiddler、postman的使用、python实现测试接口程序)
写在前面:本文主要的章节规划: 1.什么是接口测试 另外,有的时候会直接调用别的公司的接口,比如银行的.淘宝的.支付宝的,此时也需要做接口测试以及验证数据: 做接口测试的好处: 其中, ...
-
C# .net 填充无效,无法被移除 微信小程序解密失败的解决办法
微信小程序获取用户信息诸如unionId的时候需要解密,如果遇到偶然的解密失败(填充无效,无法被移除),原因很有可能是session_key错误, 也是就你用作解密的session_key并不是微信用 ...
-
thymeleaf 页面获取当前页面的完整URL地址
下面两种方法是一样的 <div th:text="${#httpServletRequest.getRequestURL() +'?'+ #httpServletRequest.get ...
-
110道python面试笔试题汇总,你能答对几道?
数据分析系列教程插播一篇面试题教程,最近公众号新来了不少朋友,这几天不少粉丝留言说问我有没有python面试题,其实之前分享过一些面试题,今天统一再给大家分享一遍,也希望能帮助此时仍在找工作的同学,尽 ...
-
怎样提供一个好的移动API接口服务/从零到一[开发篇]
引语:现在互联网那么热,你手里没几个APP都不好意思跟别人打招呼!但是,难道APP就是全能的神吗?答案是否定的,除了优雅的APP前端展示,其实核心还是服务器端.数据的保存.查询.消息的推送,无不是在服 ...
-
Spring Boot 之注解@Component @ConfigurationProperties(prefix = ";sms";) 使用@ConfigurationProperties读取yml配置
从spring-boot开始,已经支持yml文件形式的配置,@ConfigurationProperties的大致作用就是通过它可以把properties或者yml配置直接转成对象 @Componen ...