Understand in physical address, Virtual address, logical address and linear address

总结篇—涉及的硬件平台是X86

 

一、概念

Physical address

用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。

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

 

Virtual memory

这是对整个内存(不要与机器上插的那条对上号)的抽象描述。它是相对于物理内存来讲的,可以直接理解成“不真实的”,“假的”内存,例如,一个0x08000000内存地址,它并不对应物理地址上那个大数组中0x08000000-1那个地址的元素;

之所以这样,是因为现代操作系统都提供了一种内存管理的抽象,即Virtual memory。进程使用虚拟内存中的地址,由操作系统协调相关硬件,把它“转换”成真正的物理地址。这个“转换”,是所有问题讨论的关键。

有了这样的抽象,一个程序,就可以使用比真实物理地址大得多的地址空间。(拆东墙,补西墙,银行也是这样子做的),甚至多个进程可以使用相同的地址。不奇怪,因为转换后的物理地址并不相同。

——可以把连接后的程序反编译看一下,发现连接器已经为程序分配了一个地址,例如,要调用某个函数A,代码不是call A,而是call 0x08111111,也就是说,函数A的地址已经被定下来了。没有这样的“转换”,没有虚拟地址的概念,这样做是根本行不通的。

 

Logical address

Intel为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。以上为例,我们说的连接器为A分配的0x08111111这个地址就是逻辑地址。

——不过不好意思,这样说,好像又违背了Intel中段式管理中,对逻辑地址的要求,“一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为【段标识符:段内偏移量】,也就是说,上例中那个0x08111111,应该表示为【A的代码段标识符:0x08111111】,这样,才完整一些”

 

Linear address or called virtual address

跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址

 

—————————————————————————————————————————————————————————————

CPU将一个虚拟内存空间中的地址转换为物理地址,需要进行两步:首先将给定一个逻辑地址(其实是段内偏移量,这个一定要理解!),CPU要利用其段式内存管理单元,先将这个逻辑地址转换成一个线性地址,在利用其页式内存管理单元,转换成最终的物理地址。

 

这样做两次转换,的确是非常麻烦而且没有必要的,因为直接可以把线性地址抽象给进程。之所以这样冗余,Intel完全是为了兼容而已。

 

二、CPU段式内存管理,逻辑地址如何转换成线性地址

一个逻辑地址由两部分组成,段标识符:段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节,如图:

 

索引号,或者直接理解成数组下标——那它总要对应一个数组吧,它又是什么东东的索引呢?这个东东就是“段描述符”,呵呵,段描述符具体地址描述一个段(对于“段”这个字眼的理解,我是把它想象成,拿了一把刀,把虚拟内存,砍成若干的截——段)。这样,很多个段描述符,就组成了一个数组,叫“段描述符表”,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段,我刚才对段的抽象不太准确,因为看看描述符里面究竟有什么东东——也就是它究竟是如何描述的,就理解段究竟是什么东东了,每一个段描述符由8个字节组成,如下图:

 

 

这些东东很复杂,虽然利用一个数据结构来定义它,不过,我这里只关心一样,就是Base字段,它描述了一个段的开始位置的线性地址。

 

Intel设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。那究竟什么时候该用GDT,什么时候该用LDT呢?这是由段选择符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。

 

GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。

 

好多概念,像绕口令一样。这张图看起来要直观些:

 

首先,给定一个完整的逻辑地址【段选择符:段内偏移地址】,

1、看段选择符的T1=0还是1,知道当前要转换时GDT中的段,还是LDT中的段,在根据相应寄存器,得到其地址和大小。我们就有了一个数组了。

2、拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,得到Base,即基地址就知道了。

3、把Base+offset,就是要转换的线性地址了。

 

还是挺简单的,对于软件来讲,原则上就需要把硬件转换所需的信息准备好,就可以让硬件来完成这个转换了。OK,来看看Linux怎么做的。

 

三、Linux的段式管理

Intel要求两次转换,这样虽说兼容了,但是却是很冗余的,呵呵,没办法,硬件要求这样做了,软件就只能照办,怎么着也得形式主义一下。

另一方面,其他某些硬件平台,没有二次转换的概念,Linux也需要提供一个高层抽象,来提供一个统一的界面。所以,Linux的段式管理,事实上只是“哄骗”了一下硬件而已。

 

按照Intel的本意,全局的用GDT,每个进程自己的用LDT——不过Linux则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用户代码段 ,对应的,内核中的内核数据段和内核代码段。这样做没有什么奇怪的,本来就是走形式嘛,像我们写年终总结一样。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值