前言
📫作者简介:小明java问道之路,专注于研究计算机底层/Java/Liunx 内核,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计📫
🏆InfoQ签约博主、CSDN专家博主/Java领域优质创作者、阿里云专家/签约博主、华为云专家、51CTO专家🏆
🔥如果此文还不错的话,还请👍关注 、点赞 、收藏三连支持👍一下博主~
本文导读
内存在程序、Linux已经计算机中占有重要地位,本文深度解析计算机内存地址的原理,通过编译时的内存原理,深入浅出逐步讲解物理地址、虚拟内存、分段分页原理、线性地址,以及intel 对内存操作和原理解析。
作者:小明Java问道之路
链接:juejin.cn/post/713803…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一、分页原理
1、Intel分段分页原理
在了解了内碎片、外碎片、段选择子、段描述符后,我们可以开始进一步讨论。回到刚才的问题,段应该分多大才算合适?不同的程序,情况不一样。如果分得太小,就减少了内碎片,如果分得大了,就增加了外碎片,并且程序的段都是连续的,这意味着需要分配的内存是连续的。这明显不太合理。有没有进一步的解决方案呢?当然有,那就是分页。
不确定分多大,那就直接把4GB的内存空间划分为4KB一格,称为页框,同样程序的段信息(代码段、数据段)也分为4KB一个页框。然后程序的段信息和物理内存的页框一一对应即可,这样极大地减少内碎片的发生。
由于intel 最开始只支持分段,后面引入的分页也需要先分段,再分页,
从上图中可以看到,开启分页后的 intel 寻址分为两步:
1、根据段选择子和偏移量生成线性地址(linear address)。
2、根据生成的线性地址分页,获取真正的物理地址(physical address space)。
同分段一样,用 SS、DS、CS、ES 段选择子和GDTR寄存器保存段表的地址。同理,也需要页表(page table)和key来查询程序中这一页对应于物理地址的哪一页。
如何查表?查表的key在哪里很明显,物理内存被分成了4KB一个页帧,那么4GB就共有 410241024/4=1MB 个表项。假设一个表项的大小为 4byte,即 32 位,那么最直接的做法就是为每个程序都保存在一个这样的页表中,需要用分段获得的线性地址作为 key,前往表中查询对应的物理页框。这时,一个程序拥有的页表大小为 110241024*4byte=4MB 大小。
2、Intel2级分页原理
可见当程序变多后,内存几乎都被用于保存页表,这自然也不可取有没有什么办法优化呢?程序一定会用完所有的 1M 大小的表项吗?显然不可能因为程序在运行中可能由于一直没有访问某些指令和数据,导致它们的那几页没有映射到物理内存。所以根本没有必要为这些不用的页保存空的页表项。进程的这种行为,我们称之为稀疏存储。正由于这种稀疏存储的性质,我们采取了分级的页表。
如段描述符表的起始地址被保存在GDTR中一样,页目录表的地址也被保存在CR3 寄存中。线性地址的查询页表基地址的 DIR 部分为10位,Table用于查询页表项的部分为10位,用于查找物理页框偏移量部分为12位。
这样的存储,必然极大地节约空间。程序没有必要建立所有页表,使用时再在页目录和页表中创建页表项即可,没必要一开始就创建 4MB的连续空间。
总结
本文的主题虚拟地址、线性地址、物理地址到底是什么,通过intel开发手册了解其原理,顺着这个思路,已足够让读者了解计算机的内存空间了,相信读者对于更深层次的内容也能自己探究完成。