进程虚拟地址空间区域划分
代码入手
首先我们来看一段简单的代码:
#include <iostream>
using namespace std;
int gdata1 = 10;
int gdata2 = 0;
int gdata3;
static int gdata4 = 11;
static int gdata5 = 0;
static int gdata6;
int main()
{
int a = 12;
int b = 0;
int c;
static int d = 13;
static int e = 0;
static int f;
return 0;
}
那么,上述代码中各个变量分别存储在哪里?
简单图示
好了,我们的进程虚拟地址空间闪亮登场:
补充:
x86 32位体系下的4G虚拟地址空间:
Linux默认3:1来分配 user space : kernal space;
Windows默认2:2来分配 user space : kernal space。
具体解释
何为虚拟?
据说,虚拟这个词最早是由IBM公司提出的,并且解释如下:
- 它存在,你能看的见,它是物理的;
- 它存在,你却看不见,它是透明的;
- 它不存在,你却看得见,它是虚拟的;
- 它不存在,你也看不见,它被删除了。
那么,实际上,虚拟内存不存在。
为何是4G?
首先我们研究的体系是:x86 32位Linux环境
:
Linux操作系统会给当前进程分配一个2^32
大小的空间,那么,2^32换算过来就是4G了。
user space
1.保留区:
128M大小,不可访问,不允许读写。 任何普通程序对它的引用都是非法的,一般用来捕捉空指针和小整型值指针引用内存的异常情况。在定义指针时将其初始化为"NULL",它便不会被引用了,从而避免了野指针。
2.指令段【.text】、只读数据段【.rodata】:
指令段存放指令,只能读,不能写;只读数据段中存放只读数据,比如字符串常量等,只能读,不能写。
在C++中,不允许普通指针指向常量字符串,需要使用
const
:
3.数据段【.data】:
存放程序中已初始化且不为0的全局变量或静态变量。
4.数据段【.bss】:
存放程序中未初始化或者初始化为0的全局变量或静态变量。
5.堆【.heap】:
存放动态数据,需要程序员手动开辟、释放空间,在程序刚开始运行时,此区域为空,等到程序运行到手动开辟空间的指令时,此区域动态扩张。自下向上增长。
6.共享库【.dll、.so】:
动态链接库,程序在运行的过程中,将一些标准库函数映射到这里,比如C标准库函数(fread、fwrite、fopen等)。
7.栈【.stack】:
存放所有函数的活动空间,局部变量;根据程序的运行,调用函数,此区域动态地扩张和收缩。自上而下增长。
8.命令行参数:
保存传递给main函数的参数,比如argc和argv
。
9.环境变量:
用于存放当前的环境变量,在Linux下可以用env
命令查看。
kernal space
1.内存直接访问区【ZONE_DMA】:
16M大小,不需要经过CPU的寄存器,加快了磁盘和内存之间的数据交换。
2.常用区【ZONE_NORMAL】:
892M大小,内核中最重要的部分,存放页表、页面的映射、PCB。
3.高端内存区【ZONE_HIGHMEM】:
128M大小,存放大文件的映射,即内存中映射高于1GB的物理内存。64位操作系统没有该段。
解答问题
回到我们开始提出的问题:
注意:
对于a、b、c以及'{'、'}'
来说,是存储在.text
指令段的,因为他们生成的都是指令。
例如:
int a = 12
:生成汇编指令如下:mov dword ptr[a], 0Ch
参考资料
【1】Randal E. Bryant. 《深入理解计算机系统》.北京. 机械工业出版社,2016:1
【2】寻痴. 虚拟地址空间图解. CSDN. 2021-03-23
【3】聪聪菜的睡不着. 【C++】一、虚拟内存布局、编译链接原理等基础概念. CSDN. 2020-07-09