基本概念:

逻辑地址:在具有地址变换功能的计算机中,访问指令给出的操作数。

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

线性地址:逻辑地址和物理地址转换的中间层,即硬件平台页式转换前的地址。

我们都知道任何一个独立运行的程序都需要系统分配单独的内存空间,大多数情况下这个工作是由系统完成,方便程序访问变量,程序不需要关心变量的物理地址。因此现代操作系统都提供了一种内存管理的抽象,即虚拟内存。进程使用虚拟内存的逻辑地址范文,操作系统协助转换成真正的物理地址。

虚拟存储空间分布
在32位的处理器平台上,linux的存储空间和虚拟存储空间的地址范围都是从 0x00000000 到 0xFFFFFFFF
共4G,但是物理存储空间和虚拟存储空间布局完全不同。


每个进程能分配到除1G的内核空间以外的3G的独立内存空间,代码段、数据段和堆是从低地址向高地址,栈从高地址向低地址把实际的物理内存按4K的大小为一个单元标号,叫做页。

实模式和保护模式

CPU有三种工作方式:实模式、保护模式和虚拟8086模式。在刚启动的时候是实模式,只能访问1M以下的内存;操作系统运行起来就切换到保护模式,全部的32条地址总线有效,可寻址4G的物理地址空间。
以8086为例,运算器(ALU)的最大寻址数为16,地址总线为20,内存容量为1M。寻址方式为 物理地址 = 基地址 +偏移量, 基地址 =
16位的段地址 << 4; 以80386为例,CPU 和地址总线的大小相同,内存容量为4G。寻址方式  线性地址 = 基地址 + 偏移量(逻辑地址
),一般情况下逻辑地址和线性地址一致,通过Page_Tables得到物理地址。



页式管理系统地址转换
linux中的存放虚拟内存_物理内存页的对应关系是在主目录下的 /proc/pid/pagemap 文件,下图表示页式管理系统的地址转换方式,p
表示逻辑页号,f 表示物理页号,d表示页内地址。

页表的作用是实现从逻辑页号到物理页号的地址映射,以逻辑地址的检索页号检索页表得到该页的物理页号;同时将页内地址d 和物理页号拼接成实际的物理地址。
用c语言实现逻辑地址转换成物理地址
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h>
#include <assert.h> #include <fcntl.h> #include <stdint.h> int add(int a,int b)
{ return 0; } int g = 10; /* * 逻辑地址 映射得到 物理地址 * 参数为 逻辑地址 */ uint64_t
vir_to_phy(unsigned long int vaddr) { int pagesize = getpagesize(); int index =
vaddr/pagesize;//逻辑页号 p int offset = vaddr%pagesize;//页内偏移量 d int fileoffset =
index*8;//pagemap文件中读出 物理页号 int fd = open("/proc/self/pagemap",O_RDONLY); if(fd
== -1) { return 0; } lseek(fd,fileoffset,SEEK_SET); uint64_t val = 0;
read(fd,&val,sizeof(val)); uint64_t phy = 0; if(val & (uint64_t)1<<63) { val &=
(((uint64_t)1<<55)-1); phy = val*pagesize+offset;//拼接物理地址 return phy; } return
0; } int main() { int a = 0; printf("局部变量 a = %x\n",&a); printf("全局变量 g =
%x\n",&g); printf("全局函数 add = %x\n",add); printf("主程序入口 main = %x\n",main); int
*p = (int*)malloc(1024*1024*1024);//申请1G的资源 pid_t pid = fork(); if(pid == 0) {
//memset(p,0,1024*1024*1024); } //父子进程中的映射关系 int b = vir_to_phy(&p);
printf("virtul a = %x\n",&p); printf("physical a = %x\n",b); free(p); exit(0); }
运行程序我们发现,申请的资源没有使用时,父子进程返回的逻辑地址、物理地址均相同;在对子进程申请的内存赋值后,父子进程返回的物理地址不相同。也就是说,在进行fork()复制后父子进程是共享内存的,但当其中一个进程去修改其中一个数据结构时,系统会产生缺页中断,为其分配全新的空间。

参考资料: 逻辑地址转换为物理地址_百度文库
<https://wenku.baidu.com/view/b640309043323968001c925d.html>
(转)Linux中的物理和虚拟存储空间布局 - 查看主题 • Ubuntu中文论坛
<http://forum.ubuntu.org.cn/viewtopic.php?t=276918>
虚拟内存介绍及MMU工作原理(一) - CSDN博客
<http://blog.csdn.net/zxzxy1988/article/details/7384790>
逻辑地址、线性地址和物理地址的关系 - CSDN博客
<http://blog.csdn.net/prike/article/details/52722934>
实模式和保护模式 - CSDN博客 <http://blog.csdn.net/laviolette/article/details/51658650>