在内存管理中,逻辑地址是一个比较难以理解的概念,因为逻辑地址是逻辑上(思想上的,空间上)的概念,并非物理存在,很难想象出逻辑地址到底是什么样的。

        在理解逻辑地址的时候,我曾有一个疑问:假如一个操作系统最多支持64个进程,每个进程的线性地址空间为4G(32位CPU的寻址范围),那么总共需要的的内存是4G*64,这么大的内存存储在哪儿呢?

        现在,我对这个困惑的解释是:CPU是分时处理的,当时间片被分配到某一进程时,此进程的4G线性内存空间才被“激活”,CPU读取程序的逻辑地址,逻辑地址通过分段处理转化为线性地址(4G空间),线性地址再经过分页处理转化为物理地址。当时间片分配给下一进程时(进程切换),前一进程的运行环境会被保存。
        这样的处理,缓解了多任务系统环境中物理内存短缺的问题,物理内存中只会存储当前需要或经常使用的数据,其它数据保存在硬盘中,需要时再进行加载。

        程序运行的过程,就是CPU取指,取数据,执行指令,存储结果的过程;在此过程中,CPU读取的地址都是逻辑地址。
        那么程序的逻辑地址是哪里来的呢?编译时?加载时?运行时?

        在编译完成后,动态库、全局变量、静态变量、函数的逻辑地址已经“基本”确定。(编译时是不分配内存的,只是根据声明时的类型进行占位。)
        查看*.map文件和解析a.out文件得到逻辑地址,再和gdb调试时比较发现:编译出的逻辑地址和运行时的逻辑地址是一致的。
        *.map文件编译得到:g++ *.cpp -Wl,-Map,*.map
        a.out文件:objdump -D a.out | less

        实际上编译出来的逻辑地址由段选择符和段偏移地址两部分组成;可以通过段选择符在段描述符表中找到段基地址,段基地址+段偏移地址=线性地址。
        而在Linux中,段基地址是都是0,所以得到结论:逻辑地址=段偏移地址=线性地址。

        下图摘抄至《Linux内核完全注释》,可以看出Linux内存地址转换过程。

逻辑接口建立GRE隧道 逻辑接口地址_4G

 

        局部变量、堆内存的逻辑地址在运行时确定。
        由下面这个例子,我们可以看出局部变量内存逻辑地址在运行时分配。

#include <stdio.h>
#include <stdlib.h>
void  f1()
{
  static int i = 0;
  ++i;
  if(10==i)
    return;
  int *p = (int *)malloc(sizeof(int));
  printf("p===%x\n",p); //p是堆内存地址,同时又是一个局部变量,由于没有释放内存,每次运行都会分配新的内存,所以p在这里每次输出都不一样
  f1();
}
void  f2()
{ 
  static int i = 0;
  ++i;
  if(10==i)
    return;
  int *p2 = (int *)malloc(sizeof(int));
  printf("p===%x\n",p2); //p输出值是一样的
  free(p2);
  f2();  
}
int main()
{ 
  f1();
  //f2();
  return  1;
}