王道操作系统笔记,视频链接:3.1.1.3 进程的内存映像
进程的内存映像
- 用于讲解的代码程序例子:
cpp
#include <stdio.h>
#define X 1024 //宏定义的常量
int a=1; //全局变量
const int b=2; //常量
void main(){
static int c=3; //静态变量
int d=4; //局部变量
int *p = (int *)malloc(sizeof(int)*10);
a = b+c+d;
for (int i=0; i<10; i++){
p[i] = X+i;
}
printf("Hello 研究生! \n");
...
}
- 假定的内存:
- 32位系统指的是32个字节,
- 所以进程虚拟地址空间为4GB,因为刚好是 2 3 2 2^32 232字节。
- 图:

- 在第2点的图中,高地址的部分(图中操作系统内核区)会用于存储操作系统的一些内核数据,比如:
- 进程的PCB、页表
- 操作系统的代码
- 第二章中学的进程调度就属于操作系统的代码
- 例子中的程序属于用户程序,只能访问用户区的数据,不能访问操作系统内核区。
- 用户区域从最低的部分开始往上,分别用于存储:
- ①只读代码/数据(紫色部分)
- 程序代码正式运行前会经过编译、链接等一系列处理,
- 代码被翻译为与之等价的机器指令,
- 这些机器指令在程序运行过程中不会改变,所以属于只读代码,
- 运行过程中只能读取,不能写入,所以存储在这个区域。
- 还有一些比如C语言当中表示常量的const,这类不能被修改的变量也会被存储在这个区域。
- PS:C语言的宏定义中不会在内存中专门分配存储空间,
- 如代码示例的
#define X 1024,编译器会在预编译阶段将X替换为1024, - 然后1024这个值会被隐含到程序指令当中,
- 这种将常量直接放在指令中的寻址方式叫做立即数寻址。
- 注意:如果数值太大导致指令"塞不下",编译器则会将常量放入内存的只读数据段,通过地址访问------但这一般不影响我们理解宏的本质。
- 但是这是计组的内容了,这里不细究,可以自己搜索看看。
- 总之,宏定义不会存储在这一部分。
- 如代码示例的
- ②读/写数据(绿色部分)
- 结合C语言来说,就是全局变量和静态变量
- 比如代码中的a,定义在main外,是全局变量
- 代码中的c,用static关键字修饰,是静态变量
- 结合C语言来说,就是全局变量和静态变量
- 以上两个部分,在进程启动时确定大小,固定不变
- 因为这些部分本来就可以启动时确定
- ③堆(heap)(蓝色部分)
- 结合C语言来看,在C语言中,
- 但凡用malloc和free两个函数去处理的那些数据就存储在堆当中。
- 显然,这部分的大小是可以动态增加减小的,
- 因为可以动态使用malloc和free
- 对于32位系统来说,堆、读/写数据、只读代码/数据、未使用区四个部分最多为1GB
- 也就是说,除去读/写数据、只读代码/数据、未使用区三部分,
- 这1G剩下的空间可以全用于malloc和free
- 64位系统给的空间更大,这里就不展开细聊
- ④共享库的存储映射区(黄色部分)
- 系统提供的库函数,比如print函数的代码,
- 这些库函数的指令和代码就存储在这一部分,
- 不同程序使用到的库函数不一样,用的越多,占用空间越多
- 它从中间2GB的低地址部分往上
- ⑤用户栈(Stack)(红色部分)
- 程序由main函数进入,可能调用其他函数,
- 每调用一层函数,就需要保存上一层函数的信息,
- 同时需要开辟一个新的空间用于存储被调用函数的相关信息,
- 所有的和函数调用相关的数据就存储在用户栈这一片区域当中。
- 显然,这片区域也是会动态的增加或减少的。
- 调用的函数层数越多,那么用户栈也会越大,
- 函数中定义的局部变量以及函数调用的参数也存储在用户栈。
- 它从中间2GB的高地址部分往下
- ①只读代码/数据(紫色部分)
- 第5点的图:

- 可能的考察形式:
- 给一段C语言代码,询问对应的变量应该存储在哪一片区域当中
可能考法:根据C语言程序回答存储区域
