最近在看《深入理解Linux内核》,在第二章《内存寻址》中提到了逻辑地址、线性地址、虚拟地址、物理地址的概念。

原文是这么描述的:

逻辑地址(logical address):包含在机器语言指令中用来指定一个操作数或一条指令的地址。这种寻址方式在80x86著名的分段结构中表现得尤为具体,它促使MS-DOS或Windows程序员把程序分成若干段。每一个逻辑地址都由一个段(segmemt)和偏移量(offset或displacememt)组成,偏移量指明了从段开始的地方到实际地址之间的距离。 线性地址(linear address)(也称虚拟地址 virtual address):是一个32为无符号整数,可以用来表示高达4GB的地址,也就是,高大4 294 967 296个内存单元。线性地址通常用十六进制数字表示,值的范围从0x00000000到0xffffffff。 物理地址(physical address):用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。

在文中,把线性地址和虚拟地址等同,并详细定义了逻辑地址。但是,把逻辑地址的定义套入到我们平时交流中提到的逻辑地址定义,怎么这么别扭呢?

在工作中,我们经常把逻辑地址等同于虚拟地址,基本不用线性地址,例如对于以下C程序:

#include <stdio.h>#include <stdlie.h>int main(int argc, char **argv){ int var = 1; char *mvar = malloc(10); printf("var address: %p\n", &var); printf("mvar address: %p\n", &mvar);}

执行的结果

[GMPY@17:04 tmp]$./test var address: 0x7ffe9ddceb9cmvar address: 0x7ffe9ddceba0

我们在工作中交流时,经常把上面打印出来的地址叫做虚拟地址,有时候也会叫做逻辑地址,而非书中这么复杂这么绕人的概念。

那么,我们究竟怎么区分这几个地址?

关于逻辑地址的理解

在《深入理解Linux内核》中描述了在80x86处理器中地址的转换:

逻辑地址 -> [分段单元] ->线性地址 -> [分页单元] -> 物理地址

其中分段单元分页单元是硬件电路,分别用于从逻辑地址到虚拟地址从虚拟地址到物理地址的转换。注意的是,作者特意强调了在80x86处理器中

在书中Linux中的分段小章节得出一个非常重要的结论:

这可以得出另一个重要结论,在Linux下逻辑地址与线性地址是一致的,即...

在Linux下逻辑地址与线性地址是一致的!!

既然是一致的,为什么还要区分逻辑地址和线性地址?历史已经不可究,但不妨碍我 瞎想 遐想:

Intel体系下的工程师提出分段的特点时,为了区别分段与分页前的地址,苦思冥想后造出了逻辑地址的概念

为什么要强调是Intel体系呢?因为以intel为代表的有限的一些体系规定了要用分段+分页(arm体系貌似只有分页)。实际上与分段相比,Linux更喜欢使用分页方式,换句话说,Linux不喜欢用分段,但是为了兼容,只能走走形式,意思意思一下,在实现中让逻辑地址虚拟地址

结论

如果非要定义逻辑地址,我们可以把逻辑地址简单理解为

为了区分分段与分页的地址,把分段前的地址叫做逻辑地址,把分页前的地址叫做虚拟地址

在Linux中,我们可以认为逻辑地址=虚拟地址=线性地址,因此只剩下两类地址:

虚拟地址:cpu访问的地址,即C语言中"%p"打印出来的地址物理地址:物理内存实际地址,经过MMU从虚拟地址转换而来

上述仅仅是个人理解,如果有不对的地方,希望一起交流