前言
📫作者简介:小明java问道之路,专注于研究计算机底层/Java/Liunx 内核,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计📫
🏆InfoQ签约博主、CSDN专家博主/Java领域优质创作者、阿里云专家/签约博主、华为云专家、51CTO专家🏆
🔥如果此文还不错的话,还请👍关注 、点赞 、收藏三连支持👍一下博主~
本文导读
内存在程序、Linux已经计算机中占有重要地位,本文深度解析计算机内存地址的原理,通过编译时的内存原理,深入浅出逐步讲解物理地址、虚拟内存、分段分页原理、线性地址,以及intel 对内存操作和原理解析。
一、intel分段寄存器原理深度解析
1、intel分段描述
接下来一起看一个图,intel 给出的分段详解图
CS、SS等段寄存器被称为段选择子。在段描述符中查找段寄存器对应的段描述符,可发现包含有访问权限位(access)和段基址(base address),同时还需要段界限(limit)来指明这个段的段长是多少。读者可能会看到对时应物理地址(physical memory)有一个or关键字,居然出现了我们要讲的线性地址空间(linear addre ess space)。因为涉及下面要讲的分页,所以读者直接把通过段描述符查询出来的地址当作物理地址就好。
2、intel分段生成物理地址
这种查段表最终生成物理地址的原理,如下图
我们通过16位段选择子在段表中获取到了段描述符和待访问地址(如IP寄存器中的地址)相加,便得到了物理地址。读者一定要掌握,通过段表得到的基地址和段界限,仅仅用于描述一个内存段;我们需要访问其中的数据,就需要偏移量参与。
二、intel分段寄存器原理
1、16位段寄存器信息原理
1.1、16位段寄存器解析
一起来看看这些16位段寄存器中保存了哪些位,以及分别起到了什么作用。如下图描述了用于查找段描述符表中对应项的key,即段选择子子内容。
它包含3个get部分:
1、index索引(13位):段描述符表中的下标。
2、table indicator表项类型(1位):GDT表明当前程序段就是全局描述符表(global descripes table);LDT表明当前 GDT 中的表项指向的程序段是局部描述符表(localdescriptor table)。
3、requested privilege level(2位):即RPL,特权位。用于校验当前程序的执行权限,正是由于这个权限位的保护,因此应用程序和 OS 才不会发生踩踏。
对于index,这里把它当成数组索引下标即可。可以看到它的大小为 13 位,所以查询的表项最大只能拥有2^13=8192个,局限了系统的最大进程数。对于 table indicator 来说,可以这样理解,由于 GDT的表项有限,即多个程序共享,而每个程序都拥有自己的数据段、代码段、堆栈段等,所以可以利用 LDT 把它们封装在一起进行访问,这样一个程序只需要占用一部分 GDT表项。那么问题又来了,引入的LDT也是一个表。怎么查询这个表呢?同样需要 key。这时的key由谁来提供呢?仍然是段选择子。如果使用GDT,可用 CS、DS等寄存器获取到的段选择子查 GDT 表项。引入了 LDT 后,我们的段选择子可用于查LDT的表项。LDT的基地址在哪?在 GDT 中,谁来查?这时需要引入一个新的寄存器LDTR,用它作为段选择子,并前往 GDT中查询,就可以得到LDT的基地址了。
1.2、查询GDT(全局描述符表)
1、获取 ES、DS、SS、CS 等寄存器的高 13 位作为段选择子。
2、根据GDTR 寄存器获取到全局段描述符的基地址。
3、根据获取的段选择子到 GDTR 所指的全局段描述符中查找表项。
4、根据获取的表项获取到程序段的基地址。
5、根据获取的程序基地址+IP 或者其他寄存器的偏移地址就可以获取到物理地址。
1.3、查询LDT(局部描述符表)
1、根据LDTR的高13位作为段选择子到GDTR 寄存器的GDT 中查询表项。
2、根据查询的表项获取到LDT的基地址。
3、获取ES、DS、SS、CS 等寄存器的高 13 位作为段选择子。
4、根据段选择子到LDT 中查询对应的表项。
5、根据LDT的表项获取程序的段基地址。
6、根据获取的程序基地址+IP或者其他寄存器的偏移地址就可以获取到物理地址。
2、64位段寄存器信息原理
段描述符中存在那些信息
总结
本文的主题虚拟地址、线性地址、物理地址到底是什么,通过intel开发手册了解其原理,顺着这个思路,已足够让读者了解计算机的内存空间了,相信读者对于更深层次的内容也能自己探究完成。