计算机管理内存的基本方式有两种:段式管理和页式管理。而在使用80x86微处理器时,内存地址分为三个不同的地址:逻辑地址,线性地址,物理地址。他们之间有什么关系,内存是如何寻址,本文主要介绍的就是内存寻址。
1、基本概念:
cpu段式管理:段式管理的基本原理是指把一个程序分成若干个段(segment)进行存储,每个段都是一个逻辑实体(logical entity)。一个用户作业或进程所包含的段对应一个二维线形虚拟空间,程序通过分段(segmentation)划分为多个模块,故可以对程序的各个模块分别编写和编译。段式管理程序以段为单位分配内存,然后通过地址影射机构把段式虚拟地址转换为虚拟地址。
cpu页式管理: 页式管理的基本原理将各进程的虚拟空间划分成若干个长度相等的页(一般为4K),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。
逻辑地址:包含在机器语言指令中用来指定一个操作数或一条指令的地址,每个逻辑地址都由一个段和偏移量组成,表示为[段标识符:段内偏移量]。例如,在C/C++程序中我们使用指针对变量地址操作,该地址就是逻辑地址(准确的应该说是逻辑地址的段内偏移量)。对应上述段式管理,逻辑地址是段式管理转换前的程序地址。
线性地址:也称为虚拟地址,它是一个32位无符号整数,故可以用来表达高达4GB的地址。线性地址同逻辑地址一样也是不真实的地址。对应上述页式管理,线性地址是页式管理转换前的地址。
物理地址:用于内存芯片级内存单元寻址,与处理器和CPU连接的地址总线相对应。一般情况下,我们说的计算机内存条中的内存就是它(虽然不准确)。
有了上述的基本概念后,很显然,CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址,CPU要利用其段式内存管理单元,先将每个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。这就是我们所知道的段页式管理,这样两次转换的好处可以克服段式管理和页式管理的缺点。
2、CPU段式内存管理:逻辑地址转换为线性地址
逻辑地址由两部分组成:一个段标识符和一个指定段内相对地址的偏移量(简称偏移量),[段标识符: 段内偏移量]。
段标识符是由一个16位长的字段组成,称为段选择符,由处理器提供段寄存器来存放段标识符,段寄存器有6种:
(1)cs 代码段寄存器,指向包含程序指令的段;
(2)ss 栈寄存器,指向包含当前程序的段;
(3)ds 数据段寄存器,指向包含静态数据或者全局数据段;
(4)其他三个寄存器es, fs, gs称为附加段寄存器,作一般用途,可以指向任意的数据段。
偏移量指明了从段开始的地方到实际地址之间的距离,偏移量为32位。
如上图为段标识符(段选择符)格式,其中最关键的部分为索引号。
段标识符,按照其字面意思便可理解它的作用是用来标识一个段的,而段是如何表示的呢?
这就引申到了另外一个概念:段描述符,每个段由一个8字节的段描述符表示,它描述了段的特征(段描述符就是段)。段标识符通过索引号(13位)就可以找到其对应的段(段描述符),段的格式如下图所示:
段描述符中我们需要关注的字段为:Base,它描述了一个段的开始位置的线性地址。
段描述符放在全局描述符表(GDT,存放于gdtr寄存器中)或局部描述符表(LDT,存放于ldtr寄存器中)中,通常只定义一个GDT,而每个进程除了存放在GDT中的段之外如果还学要创建附加的段,就可以有自己的LDT。。
好了,通过上述的讲解,我们就可以关注一个逻辑地址是怎样转化成相应的线性地址的,具体步骤如下:
(1)先检查逻辑地址的段选择符的TI字段,以决定段描述符保存在哪一个描述符表中。(TI=0表明在GDT中,TI=1表明存在LDT中)
(2)根据段选择符的索引号,计算查找段描述符的地址,方法:索引号*8 + gdtr或者ldtr寄存器中的内容 = Base。
(3)把逻辑地址的偏移量与步骤(2)中得到的Base字段值相加就可以得到其对应的线性地址。
逻辑地址的转换图如下图所示:
3、CPU页式内存管理:线性地址转换为物理地址
首先,我们得知道一些线性地址相关的东西。
(1)线性地址被分成固定长度为单位的组,称为页。页内部连续的线性地址被映射到连续的物理地址中。
(2)分页段元把所有的物理地址分成固定长度的页框,称为物理页。
(3)线性地址映射到物理地址的数据结构称为页表。
(4)32位的线性地址,被分成3个域:目录(Directory)高10位,页表(Table)中间10位,偏移量(Offset)低12位,由偏移量的12bit可知,每页含有4096字节的数据。
线性地址的转换分两步完成,每一步都基于一种都基于一种转换表,第一种转换表称为页目录表转换,第二种转换称为页表转换。使用这种二级模式的目的在于减少每个进程页表所需的RAM的数量。就像我们看书有个书目录一样,方便快捷。具体转换如下图所示:
转换步骤:
(1)从cr3中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对应寄存器);
(2)根据线性地址前十位,在数组中,找到对应的索引项,因为引入了二级管理模式,页目录中的项,不再是页的地址,而是一个页表的地址。(又引入了一个数组),页的地址被放到页表中去了。
(3)根据线性地址的中间十位,在页表(也是数组)中找到页的起始地址;
(4)将页的起始地址与线性地址中最后12位相加,得到最终我们想要的其对应的物理地址。
参考:
(1)深入理解linux内核