1.内存寻址的基本流程
首先,一般的书中或者博客中,在介绍Linux内核的时候,首先都会介绍Linux的内存寻址,这样的一块知识和程序的关联在哪里,下面的内容就首先的讨论一下这个问题。
在我们书写程序的时候,我们有的时候会操作地址空间空间,那么我们用到的地址是真实的内存空间地址么,答案是否定的,我们用到的是逻辑地址,在操作系统中,存在着这样的三个地址概念,他们是逻辑地址、线性地址和物理地址。
逻辑地址:包含在机器语言中,用来指定一个操作数或者一条指令的地址。每一个逻辑地址都由一个段和偏移量组成,偏移量指明了他从段开始的地址到实际地址的距离;
线性地址:也称作虚拟地址,是一个32位的无符号整数(32位操作系统),可以表达4GB的地址空间。其地址用十六禁止的数字表示,值得范围从0x00000000到0xffffffff。
物理地址:用于内存单元的寻址,是真正的内存的地址。
三种地址之间的关系如下:
从80286模型开始,Intel的处理器以两种不同的方式进行地址转换,这两种方式分别是实模式和保护模式,下面我们描述的都是保护模式下的地址转换。
2.内存中的分段机制
首先,我们来讨论下CPU的寻址方法:
(1)根据指令的类型来确定应该使用哪一个段寄存器,例如转移指令中的地址在代码段,取数指令中的地址在数据段。
(2)根据段寄存器中的内容,找到相应的“地址段描述符”(下面会说的)。
(3)从段描述符中找到段的基址。
(4)将指令发出的地址作为位移,与段结构中规定的段长度相比较,看看是都越界。
(5)根据指令的性质和段描述符中的规定的权限进行比对,查看是否越权。
(6)将指令发出的地址作为位移,和基址相加,得出线性地址。
在CPU中,有两个寄存器,一个是全局性段描述表寄存器GDTR,另一个是局部性段描述表寄存器LDTR,分别用来指向存储在内存一段描述结构数组,或者成为段描述表。
在此基础上,段寄存器中的高13位用作访问段描述符中具体的描述的下标(index)。下面就是段寄存器的结构:
因此,GDTR或者LDTR中的段描述表指针和段寄存器给出的下标,结合起来,才能够找到正确的段描述符的位置,可以理解成,将段寄存器的低三位屏蔽掉以后与GDTR或者LDTR的基址相加,才能得到正确的描述符的准确的位置。
3.内存中的分页机制
分页单元把得到的线性地址转换成物理地址。其中一个关键的任务是把请求的访问类型与线性地址的访问权限相比较,如果这次内存访问是无效的,就产生一个缺页异常。为了提高效率,线性地址被分成固定长度的单元组,称之为页,页内部连续的线性地址映射到连续的物理地址中。因此,内核可以指定一个页的物理地址和他的存取权限,而不用指定页包含的全部地址的存取权限。
分页就是把所有的RAM分成固定长度的页框。每一个页框包含一个页,也就是说一个页框的长度和一个页的长度是一致的。页框是主存的一部分,因此也就是一个存储的区域。把线性地址映射到物理地址的数据结构称之为页表。页表存放在主存中,并在启用分页单元之前必须由内核对页表进行初始化。
在常规的分页中,通常将分页单元处理成4K的页,32位的地址被分成3个域,如下所示:
从上面的结构我们可以看出,页面中的目录一共有2的10次方个,每一个目录项指向一个页表,而每一个页表有2的12次方个页面描述项,在CPU中,增加了CR3寄存器作为指向当前页面目录的指针。这样其映射 的过程和流程图如下:
(1)从CR3中取得页面目录的基地址;
(2)以线性地址中的dir为下标,在页面目录中,取得相应的页面表的基地址;
(3)以线性地址的page段位为下标,在(2)中得到的页面表中,取得相应的页面描述符;
(4)将页面描述符中的基址与相应的偏移量相加得到相应的物理地址。
从上面的流程,我们可以看出来,从线性地址到物理地址使用的了两步转换的模式,之所以使用这种模式,是为了减少每个进程页表所需要的RAM数量,如果将dir和page两个合并,那么将是20位,页面表就有1M个表项,由于每个大小是4K,因此将在4G的内存上进行地址变换,但是其实一个进程根本就没有那么大,所以大部分的表项是空着的,空着的也要占内存,采用两层的模式,采用的是那些虚拟内存区请求页表来减少内存的容量。
4.物理内存的布局
在初始化的阶段,内核必须建立一个物理地址的映射来指定哪些物理地址的范围对内核可用,而那些是不可用的(或者因为 他们映射硬件设备I/O的共享内存,或者因为相应的页框含有BIOS数据)。
一般的来说,Linux安装在RAM地址从0x00100000开始的地方,也就是说,从第二个MB的地方开始。所需要的页框总数以来与内核的解决方案:典型的配置所得到的内核安装在小于3MB的RAM中。之所以不是从0页框开始安装,是因以下的几点原因:
·页框0是由BIOS上使用的,存放加电自检期间检查到的系统硬件配置。因此很多膝上电脑的BIOS甚至在系统初始化以后还将数据写到该页框中。
·物理地址从0x000a0000到0x000fffff的范围通常留给BIOS例程,并且映射ISA图形卡上的内部内存。这个区域就是所有的IBM兼容PC上从640K到1MB著名的洞:物理地址存在但是被保留,对应的页框不能由操作系统使用。
·第一个MB的其他的页框可能由特定的计算机模型保留。例如Thinkpnd把0xa0页框映射到0x9f页框。
下图显示Linux填充的3MB的RAM
进程的线性页表的线性地址空间分成两部分,从0x0000000到0xbfffffff的线性地址,无论是进程运行在用户态还是内核态都可以进行寻址,从0xc0000000到0xfffffff的线性地址,只有内核态的进行才能进行寻址,在下面的图中,给了整个内存的完成布局。
来源:https://www.cnblogs.com/MPoooooo/p/4486283.html