linux内核原理剖析——内存寻址(一)

最近总想分享点硬核的原创文章出来,一是硬核技术是一个程序员真正应该修炼
的内功;二是修炼硬核技能是通往架构师领域的必经之路。本系列文章将分享关
于linux内核设计原理相关的内容,希望能打通我们的七经八脉,真正领悟底层系
统设计的核心思想。关于linux内核之内存寻址相关的原理计划分享出三篇文章来讲解,此文为第一篇。

引言

所谓内存寻址,简单说来就是cpu接受到指令后需要从内存中取得相应数据,但是内存中的数据都是有对应地址的,需要通过地址来获取相应地址段上的数据,这也就是为什么要内存寻址。

所谓地址在操作系统中分为逻辑地址、线性地址、物理地址。

  • 逻辑地址
    逻辑地址其实就是机器码指令用到的地址,也就是机器指令码中用到的地址都是逻辑地址。目前这个地址是由16位段选择符和32位偏移量来表示的,这个地址也可叫做虚拟地址。

  • 线性地址
    是一个32位无符号整数,是由逻辑地址转换而来的。

  • 物理地址
    是内存芯片中的物理地址,是缓存数据的实际地址。是由逻辑地址转换而来的。最终是由这个地址来定位到内存空间。

实模式与保护模式

在8086处理器出现之前,内存地址的寻址方式是直接访问物理地址。在8086处理器中,了寻址1M的内存空间,把地址总线扩展到20位,但是ALU只有16位,也就是说如何用16位寄存器表示20位内存空间?为了解决这个问题,就产生了分段机制,也就是引入了4个段寄存器,es、cs、ds、ss,每个寄存器都是16位。8086处理器中逻辑地址格式为16位段寄存器地址:16位偏移地址,那么怎么用两个16位地址表示20位内存空间呢?也就是怎么将逻辑地址转换成实际的20位物理地址呢?最终就将16位段寄存器地址左移4位加上偏移地址,即物理地址=段寄存器地址*16 + 偏移地址。这个物理地址对于程序员来说是可见的,所有的段都是可读、可写、可执行的,这种模式叫做实模式(实模式也就是物理地址对应程序员是可见的)。但是正因为这种模式的不安全性,便产生了另外一种模式,保护模式。

从80286和x86开始就引入了安全模式,也就是保护模式(物理地址是通过转换映射并且提供了安全验证)。在80286的保护模式也只是一个过渡,在80286的情况下总线地址变为了24位,但是寄存器还是16位,也就是内存寻址空间达到了16M,但是单个寄存器的寻址空间仍为64k。但是此状况在80386处理器中就得到了解决,总线地址和寄存器都变为了32位,单个寄存器的寻址空间达到了4G,不但寻址能力得到了提高,其为了提高安全性,引入了段描述符表的概念,也就是不是所有的段在用户空间都可以访问,关于上述分段机制、段描述符表等原理在下面章节都会介绍。

分段原理

分段机制就是将逻辑地址转为线性地址,同时也增加安全性。

段选择符

一个逻辑地址是由16位段选择符和32位偏移量组成。
段选择符
为了加快查找段选择符的速度,处理器提供了段寄存器,段寄存器主要目的就是为了存储段选择符。提供了6种段寄存器,分别是cs、ds、es、ss、fs、和gs。

  • cs:代码段寄存器。指向存储程序代码指令。其中有2位知明当前cpu的特权级别,0为内核态,3为用户态,也就是对应linux系统中的用户态和内核态。
  • ds:数据段寄存器。指向包含静态数据或全局数据。
  • ss:栈段寄存器。指向当前程序的栈。

其他三种寄存器作为普通用途。

段描述符

每个段有一个8个字节的段描述符,段描述符描述了段的基本信息,如段的基地址、特权级别等。存在全局段描述符和局部段描述符,全局段描述符放在全局段描述符表(GDT)中,局部段描述符放在局部段描述符表中(LDT)中。

段描述符字段:

Base:包含段的首字节的线性地址。
Limit:存放改段最后一个字节的偏移量。
DPL:标识当前段描述符的特权级别。为0即是内核态,也就是访问这个段需要内核态(看段选择符的CPL的值),为3即使用户态,也就是所有段选择符都可以访问。

上面是几个主要的段描述符字段,当然还有其他的字段,大家感兴趣的可以自行查找。

段描述符分类:
段描述符
段描述符分为上面三个类型,其中可以看到Base(首字节的线性地址)占32位,也包含了我上面说过的Limit和DPL。

快速查找段描述符

为了快速由段选择符查找到段描述符,80x86处理器提供了非编程寄存器(程序员不可访问),这个寄存器存储了段描述符,这样逻辑地址转换为线性地址就不需要每次都访问存放在主内存中的GDT了,直接访问寄存器中的段描述符就可以了,这样就加快了转换速度。
段选择符找段描述符

逻辑地址转线性地址原理

逻辑地址转为线性地址其实是用到了一个叫分段单元的硬件:
逻辑地址到线性地址的转换过程
上图说明了逻辑地址转为线性地址的过程。即首先通过逻辑地址中的TI来找到改逻辑地址是属于哪个段描述符(GDT/LDT),然后得到相应的GDT/LDT的地址(假设为a),那么a+逻辑地址中的index*8找到段描述符,线性地址即为找到的段描述符的Base地址+逻辑地址中的偏移地址。

最后

很晚了,就先分享到这吧,主要讲解了关于内存寻址的分段部分。下一篇将继续关于内存寻址的分享,会讲解关于分页单元、线性地址转为物理地址的原理。如果本文对你有价值,哪怕一点点,请记得关注、点赞,有不足或疑问之处请留言。下一篇关于内存寻址的文章计划在本周内分享。

发布了7 篇原创文章 · 获赞 6 · 访问量 997

猜你喜欢

转载自blog.csdn.net/qq_20878967/article/details/105655588