Linux内核学习笔记:内存寻址
内存地址分为三种:逻辑地址,线性地址,物理地址。在分段的CPU结构中,程序中引用的地址都是逻辑地址,逻辑地址经过分段单元成为线性地址。然后经过分页单元成为物理地址,物理地址就是硬件电路寻址的实际地址。如果CPU体系结构不支持分段,那么逻辑地址等于物理地址。一般RSIC指令的CPU都不支持分段,如arm。复杂指令的CPU支持分段,如x86。
一.分段
1.硬件中的分段:因为x86体系结构是分段的,所以程序是由很多段组成。程序的逻辑地址有段与偏移量组成。实现这种分段机制有特殊的硬件来完成,主要有段寄存cs, ss, ds, es, fs, gs,全局段描述符寄存器gdtr与局部段描述符寄存器ldtr。前三个段寄存器有固定的用途:cs 代码段寄存器。ss 栈段寄存器。 ds数据段寄存器。逻辑地址由16位段选择符和32位偏移量组成,段寄存器装有段选择符,段选择符由索引号与特权级。当段选择符装入段寄存器时,硬件单元就会按照里面的索引号,根据是全局段还是局部段从gdtr或ldtr寄存器段描述符的初始地址,算出线性地址。在这期间还进行相应的权限检查。
2.linux中的分段:linux的设计目标就是简单实用就好,而分段单元使得程序变得复杂。所以Linux不喜欢分段。但在在x86平台上必须分段,所以linux采用一种巧妙的方法,在用户态与内核态使用四个段,分别是用户代码段,用户数据段,内核代码段,内核数据段。而且段描述符的基地址都是0,这样换算下来的逻辑地址等于线性地址。分段的唯一用途就是可以检查权限。
二. 分页
1. 硬件中的分页:分页使得线性地址转换成物理地址,还有一个关键的任务就是进行线性地址访问权限的检查。为了方便,线性地址分为相应的组:称为页。而物理地址也相应的分为组:称为页框。页的大小由处理器决定。有些处理器支持不同大小的也,如s3c2440。x86支持4k大小的也。分页根据页的大小分为常规分页与扩展分页。扩展分页允许页的大小为4M而不是4k。常规分页采用三级页表,32位线性地址分为三个部分,最高十位是目录中间十位为页表,最后12位偏移量。在打开硬件分页功能前,首先应该初始话页表,然后把页目录的物理地址存放在cr3寄存器中,线性地址转换成物理地址是这样进行的。首先分页单元取线性地址的最高十位,以它为索引找到页目录项,然后取页目录中的base字段加上线性地址的中间十位找到页表项,然后加上线性地址的最后12位偏移量就是物理地址。当然这其中与做相应的权限检查。
2. linux中的分页:为了保持可移植性,无论硬件采用几级分页,linux都采用的四级分页:页全局目录,页上级目录,页中间目录,页表。如果硬件是两极分页,linux采用中间两极分页全是0的方式消除中间的分页。linux的进程很大程度上依赖于分页技术。不同的进程分配不同的地址空间,有效的减少了寻址错误。分页技术是虚拟内存机制的基础。linux提供了一些宏来简化页表的操作。
三. 物理内存布局
在系统初始化的时候,linux必须对物理内存做一定的映射,指定那些物理内存可以用,那些不可以用。内核保留的页框为:不可用的物理地址的页框范围,这个也就是没有安装物理内存的地址。另外一个是含有内核代码和已初始化数据结构的页框。由于硬件的原因,linux一般安装在物理地址的0x00100000开始的地方,但也不是绝对的,如mini2440,物理地址开始就是0x30000000,所以内核安装在0x30008000起始地址处。在启动内核的早期阶段,内核访问BIOS来确定物理内存的大小,有的体系结构通过启动代码传过来的内核参数来了解物理内存的大小。随后内核运行一些函数来初始化内存映射,初始化内核的一些表量来描述内存的物理布局。