物理地址逻辑地址虚拟地址的概念

上传人:ji****72 文档编号:37992563 上传时间:2018-04-25 格式:DOC 页数:8 大小:177KB
返回 下载 相关 举报
物理地址逻辑地址虚拟地址的概念_第1页
第1页 / 共8页
物理地址逻辑地址虚拟地址的概念_第2页
第2页 / 共8页
物理地址逻辑地址虚拟地址的概念_第3页
第3页 / 共8页
物理地址逻辑地址虚拟地址的概念_第4页
第4页 / 共8页
物理地址逻辑地址虚拟地址的概念_第5页
第5页 / 共8页
点击查看更多>>
资源描述

《物理地址逻辑地址虚拟地址的概念》由会员分享,可在线阅读,更多相关《物理地址逻辑地址虚拟地址的概念(8页珍藏版)》请在金锄头文库上搜索。

1、一、概念物理地址(physical address)用于内存芯片级的单元寻址,与处理器和 CPU 连接的地址总线相对应。这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从 0 字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽像,内存的寻址方式并不是这样。所以,说它是“与地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。也许错误的理解更利于形而上的抽像。虚拟内存(virtual memory)

2、这是对整个内存(不要与机器上插那条对上号)的抽像描述。它是相对于物理内存来讲的,可以直接理解成“不直实的”,“假的”内存,例如,一个 0x08000000 内存地址,它并不对就物理地址上那个大数组中 0x08000000 - 1 那个地址元素;之所以是这样,是因为现代操作系统都提供了一种内存管理的抽像,即虚拟内存(virtual memory)。进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。这个“转换”,是所有问题讨论的关键。有了这样的抽像,一个程序,就可以使用比真实物理地址大得多的地址空间。(拆东墙,补西墙,银行也是这样子做的),甚至多个进程可以使用相同的地

3、址。不奇怪,因为转换后的物理地址并非相同的。可以把连接后的程序反编译看一下,发现连接器已经为程序分配了一个地址,例如,要调用某个函数 A,代码不是 call A,而是 call 0x0811111111 ,也就是说,函数 A 的地址已经被定下来了。没有这样的“转换”,没有虚拟地址的概念,这样做是根本行不通的。打住了,这个问题再说下去,就收不住了。逻辑地址(logical address)Intel 为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。以上例,我们说的连接器为 A 分配的 0x08111111 这个地址就是逻辑地

4、址。不过不好意思,这样说,好像又违背了 Intel 中段式管理中,对逻辑地址要求,“一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 段标识符:段内偏移量,也就是说,上例中那个 0x08111111,应该表示为A 的代码段标识符: 0x08111111,这样,才完整一些”线性地址(linear address)或也叫虚拟地址(virtual address)跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。-CPU 将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个

5、逻辑地址(其实是段内偏移量,这个一定要理解!),CPU 要利用其段式内存管理单元,先将为个逻辑地址转换成一个线程地址,再利用其页式内存管理单元,转换为最终物理地址。这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽像给进程。之所以这样冗余,Intel 完全是为了兼容而已。2、CPU 段式内存管理,逻辑地址如何转换为线性地址一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个 16 位长的字段组成,称为段选择符。其中前13 位是一个索引号。后面 3 位包含一些硬件细节,如图:最后两位涉及权限检查,本贴中不包含。索引号,或者直接理解成数组下标那它总要对应一个数组吧

6、,它又是什么东东的索引呢?这个东东就是“段描述符(segment descriptor)”,呵呵,段描述符具体地址描述了一个段(对于“段”这个字眼的理解,我是把它想像成,拿了一把刀,把虚拟内存,砍成若干的截段)。这样,很多个段描述符,就组了一个数组,叫“段描述符表”,这样,可以通过段标识符的前 13 位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段,我刚才对段的抽像不太准确,因为看看描述符里面究竟有什么东东也就是它究竟是如何描述的,就理解段究竟有什么东东了,每一个段描述符由 8 个字节组成,如下图:这些东东很复杂,虽然可以利用一个数据结构来定义它,不过,我这里只关心一样,

7、就是 Base 字段,它描述了一个段的开始位置的线性地址。Intel 设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。那究竟什么时候该用 GDT,什么时候该用 LDT 呢?这是由段选择符中的T1 字段表示的,=0,表示用 GDT,=1 表示用 LDT。GDT 在内存中的地址和大小存放在 CPU 的 gdtr 控制寄存器中,而 LDT 则在 ldtr 寄存器中。好多概念,像绕口令一样。这张图看起来要直观些:首先,给定一个完整的逻辑地址段选择符:段内偏移地址,1、看段选择符的 T1=0 还是 1,知

8、道当前要转换是 GDT 中的段,还是 LDT 中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。2、拿出段选择符中前 13 位,可以在这个数组中,查找到对应的段描述符,这样,它了 Base,即基地址就知道了。3、把 Base + offset,就是要转换的线性地址了。还是挺简单的,对于软件来讲,原则上就需要把硬件转换所需的信息准备好,就可以让硬件来完成这个转换了。OK,来看看 Linux 怎么做的。3、Linux 的段式管理Intel 要求两次转换,这样虽说是兼容了,但是却是很冗余,呵呵,没办法,硬件要求这样做了,软件就只能照办,怎么着也得形式主义一样。另一方面,其它某些硬件平

9、台,没有二次转换的概念,Linux 也需要提供一个高层抽像,来提供一个统一的界面。所以,Linux 的段式管理,事实上只是“哄骗”了一下硬件而已。按照 Intel 的本意,全局的用 GDT,每个进程自己的用 LDT不过 Linux 则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用户代码段,对应的,内核中的是内核数据段和内核代码段。这样做没有什么奇怪的,本来就是走形式嘛,像我们写年终总结一样。include/asm-i386/segment.hCopy to clipboard - CODE:#define GDT_ENTRY_DEFAULT_USER_CS 14#define

10、 _USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)#define GDT_ENTRY_DEFAULT_USER_DS 15#define _USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)#define GDT_ENTRY_KERNEL_BASE 12#define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0)#define _KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)#define GDT_ENTRY_KERNEL_DS (GDT_ENT

11、RY_KERNEL_BASE + 1)#define _KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)把其中的宏替换成数值,则为:Copy to clipboard - CODE:#define _USER_CS 115 00000000 1110 0 11#define _USER_DS 123 00000000 1111 0 11#define _KERNEL_CS 96 00000000 1100 0 00#define _KERNEL_DS 104 00000000 1101 0 00方括号后是这四个段选择符的 16 位二制表示,它们的索引号和 T1 字段值也可

12、以算出来了Copy to clipboard - CODE:_USER_CS index= 14 T1=0_USER_DS index= 15 T1=0_KERNEL_CS index= 12 T1=0_KERNEL_DS index= 13 T1=0T1 均为 0,则表示都使用了 GDT,再来看初始化 GDT 的内容中相应的 12-15 项(arch/i386/head.S):Copy to clipboard - CODE:.quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */.quad 0x00cf9200000

13、0ffff /* 0x68 kernel 4GB data at 0x00000000 */.quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */.quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */按照前面段描述符表中的描述,可以把它们展开,发现其 16-31 位全为 0,即四个段的基地址全为 0。这样,给定一个段内偏移地址,按照前面转换公式,0 + 段内偏移,转换为线性地址,可以得出重要的结论,“在Linux 下,逻辑地址与线性地址总是一致(是一致

14、,不是有些人说的相同)的,即逻辑地址的偏移量字段的值与线性地址的值总是相同的。!”忽略了太多的细节,例如段的权限检查。呵呵。Linux 中,绝大部份进程并不例用 LDT,除非使用 Wine ,仿真 Windows 程序的时候。4.CPU 的页式内存管理CPU 的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。从管理和效率的角度出发,线性地址被分为以固定长度为单位的组,称为页(page),例如一个 32 位的机器,线性地址最大可为 4G,可以用 4KB 为一个页来划分,这页,整个线性地址就被划分为一个 tatol_page220的大数组,共有 2 的 20 个次方个页。这个大数组我

15、们称之为页目录。目录中的每一个目录项,就是一个地址对应的页的地址。另一类“页”,我们称之为物理页,或者是页框、页桢的。是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。这里注意到,这个 total_page 数组有 220 个成员,每个成员是一个地址(32 位机,一个地址也就是 4 字节),那么要单单要表示这么一个数组,就要占去 4MB 的内存空间。为了节省空间,引入了一个二级管理模式的机器来组织分页单元。文字描述太累,看图直观一些:如上图,1、分页单元中,页目录是唯一的,它的地址放在 CPU 的 cr3 寄存器中,是进行地址转换的开始点。万里长征就从此长始了。2、每一个活动的进程,因为都有其独立的对应的虚似内存(页目录也是唯一的),那么它也对应了一个独立的页目录地址。运行一个进程,需要将它的页目录地址放到 cr3 寄存器中,将别个的保存下来。3、每一个 32 位的线性地址被划分为三部份,面目录索引(10 位):页表索引(10 位):偏移(12 位)依据以下步骤进行转换:1、从 cr3 中取出进程的页目录地址(操作系统负责在调度进程的时候,把这个地址装入对

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号