32位Linux的进程虚拟地址空间
任何的编程语言—》产生两种东西:指令和数据
C++代码编译,链接以后,会产生一个.exe可执行文件,放在磁盘上。CPU不可能直接运行磁盘上的程序。CPU首先把程序从磁盘上加载到内存当中,那么它把可执行程序的哪些东西加载到内存当中,加载到内存当中如何存放?内存有没有区域的划分,划分了以后是什么样子?
我们知道,是不可能直接加载到物理内存中的。
我们是在X86 32为linux环境开发的。
当我们一个程序运行的时候,linux系统(内核)会给当前进程分配1个2^32位大小的一块空间(4GB)。(64位,2的64方)
我们称作进程的虚拟地址空间
虚拟的概念:它不存在,你看得见,它是虚拟的
虚拟地址空间只是内核创建的一系列数据结构而已。
进程虚拟地址空间被默认划分成2个部分:
- 默认用户空间占3G,内核空间占1G。
- 可以通过修改操作系统的配置,来调整每一个进程基于虚拟地址空间中用户空间和内核空间的大小比例。
- 注意: 每一个进程都有这样一个虚拟地址空间
1、用户空间
用户空间是怎么划分的?
不能访问段
- 并没有从0地址开始存放内容,前面空了一段(0~0x08048000),是预留的,不能访问的。
执行上面代码,程序直接崩溃,不能访问0地址,不能读不能写。
代码段(.text段)和只读数据段(.rodata)
指令 在程序运行的时候放在内存的 代码段(.text段)!
-
指针p在栈上,“hello world"字符串在.rodata只读数据段
-
.text段和.rodata段只能读,不能写
-
所以在比较新的编译器中,不允许用普通指针指向字符串,需要在前面加上const,就会避免误写 了
.data段
- .data段放的是数据,专门存放的是已初始化的,而且初始化不为0的数据。
- 存放的是已经初始化的全局变量,静态变量,和常量。
.bss段
.bss段用来存放:
- 未初始化的全局变量和静态局部变量
- 初始值为0的全局变量和静态局部变量(依赖于编译器实现)
- 未定义且初值不为0的符号(该初值即common block的大小)
- .bss段也是数据段
- (程序运行的时候,内核给当前进程分配地址空间,把未初始化的数据或者初始化为0的数据放在.bss段,操作系统内核负责把.bss段的全部数据置为0)
我们在全局的作用域上去写全局变量,没有初始化,我们去打印它的值,是0,因为存放在.bss段。
程序运行时,内核给当前进程分配地址空间,将未初始化的数据放在.bss段,内核将.bss段数据全部置为0;
.heap堆
- .heap堆(这是个空洞,程序运行以后在调用new或者malloc的时候,才会在这里分配堆内存)(从上到下,即低地址到高地址分配)
- 堆是从低地址到高地址
- 堆内存区的地址是不连续的,它是系统将空闲内存块链接起来的链表,用户用new/malloc请求分配时,找到第一个满足大小要求的块从链表中删除此节点,然后分给用户,没有栈内存速度快,但是很灵活。
加载共享库(动态链接库)
加载共享库(动态链接库)(Windows上是.dll,linux上是.so库)
stack空间
- stack空间(函数运行或者产生线程池,每个线程独有的栈空间)
- 局部变量存放在栈上
- 栈从高地址到低地址,从下往上增长
- 栈内存区的地址是连续的,由系统控制速度较快
命令行参数和环境变量
2、内核空间
内核空间主要分为了3个区域:
- DMA—16M
- NORMAL—800多M(存进程内核空间PCB块,进程控制块,内核空间的线程,内核函数在运行时所依赖的栈空间)
- HIGHMEM----高端内存,做地址映射
下图中:
- 红色框内产生的都是指令,存放在.text段
- 褐色和蓝色的都是数据,在.data和.bss段存储。
问题:a,b,c不是局部变量吗,为什么在text段,应该在栈上啊?
- a,b,c三行代码编译后是3个指令,是放在text段上的;
- 三行代码指令运行时,系统在当前栈上给函数开辟一个栈帧,会在栈上划分出空间存放局部变量。
打印c的值一定不是0,它是栈上的无效值;
打印g的值一定为0,g在.bss段;
#include <iostream>
using namespace std;
//全局变量,都叫做数据,在编译或者符号表中都会产生符号
int gdata1 = 10;//.data段
int gdata2 = 0;//.bss段
int gdata3;//.bss段
static int gdata4 = 11;//.data段
static int gdata5 = 0;//.bss段
static int gdata6;//.bss段
int main()
{
//不会产生符号,对应生成指令
//这3个局部变量,产生move指令,存放在.text段
int a = 12;//指令运行的时候在栈上划分4字节的空间来存放12
int b = 0;
int c;
//下面这3个静态局部变量是放在数据段的,程序启动的时候不会初始化,第一次运行到它们才初始化
static int e = 13;//.data段
static int f = 0;//.bss段
static int g;//.bss段
return 0;
}
每一个进程的用户空间是私有的,但是内核空间是共享的!!!
如果创建多个进程的话,它们的用户空间的私有的,但是它们的内核空间是共享的。
举例:
- 进程间的通信方式:匿名管道通信(在内核空间划分一块内存,进程1往这块共享内存写数据,进程2进程3就可以看见)