我们使用编译器所看到的内存地址,全部都是虚拟地址!
例如:
int num=0; cout<<&num<<endl;
这时输出的内容就是虚拟地址
再如:
(Linux下程序运行结果如下,其他环境可能有差别)
解释:
这个程序有一个全局的变量 g_val=0
fork创建子进程之后,子进程中修改了g_val=100
父进程之后读取g_val的值还是0,而且父子进程输出g_val的地址是一样的
一个物理地址的内容是不可能是两个值,我们编译器看到的地址是虚拟地址。
操作系统负责将 虚拟地址 转化成 物理地址 。物理地址,用户一概看不到,由操作系统统一管理。
进程虚拟空间:
各个数据节存放的数据类型介绍如下:
(1) 代码段(.text): 也称文本段,由编译器产生,存放程序的二进制机器码和只读数据,比如程序的可执行指令。.text 段是只读的,且大小在运行时是固定的。任何尝试向.text段写入信息的都会导致段错误。
(2) 数据段: 用于存储全局变量等。其主要包括:
①已初始化的数据段(.data): 存放全局和静态的已初始化变量。本节占用的内存大小在运行时也是固定的,标记为只读。
②末初始化的数据段(.bss): 存放全局和静态的未初始化变量。本节占用的内存大小运行时也是固定的。该段应标记为可读可写,但不可执行。
(3)堆(heap): 用来存储程序运行时动态分配的变量。堆内存的分配释放一般由程序控制,其分配由 new(),malloc() 等实时内存分配函数实现。如果程序没有释放该部分内存,则在程序结束后由系统回收。
(4)栈stack):用来存储函数调用信息和临时变量的数据结构,程序运行时根据需要分配,在不需要时自动清除。
(5)环境变量/命令行参数: 用于存储进程运行时可能用到的系统变量的副本。例如,运行时进程可访问的路径、shell 名称等。该节是可读可写的。除此之外,命令行参数也存储在该区域。
在默认情况下Windows系统将高地址的2 GB空间分配给操作系统(也可配置1 GB),而Linux系统将高地址的1 GB空间分配给内核。这些分配给内核的空间叫内核空间,用户不可访问,用户使用剩下的空间称为用户空间。
文章开始部分的程序,g_val是已初始化的全局变量,存在 .data 部分
虚拟的进程地址通过页表映射到了物理内存的不同地方
(本文虚拟地址部分摘自 《网络安全》张立江)