引子:栈区的内存分配是从高地址往低地址分配,而堆区是低地址往高地址分配;被“static”修饰的局部变量,保存在了全局变量区而不是栈区;“fork()”函数打开子进程后,会惊奇的发现,局部变量的地址和父进程一致,就算修改了的局部变量的地址也是和父进程对应局部变量的地址一致,这就导致了一个地址不同的进程访问值是不同的;这是因为我们所说的地址都是虚拟地址(线性地址)而不是物理地址;
一、进程地址空间
一个进程包含“内核数据结构 + 可执行程序(代码 + 数据)”,而具体来说,“内核数据结构”就是一个“PCB + 进程地址空间 + 页表”,其中进程地址空间的排布就如上图所示;虚拟地址和实际物理地址通过“页表(k-v结构:虚拟地址-物理地址-访问权限-标志位:CPU中cr3寄存器保存了页表的物理地址,该内容是进程的上下文,进程结束后会被存储在PCB中:物理内存没有读写执行的概念,页表加上访问权限,如果是只读内容比如代码和常量,会在页表转化的时候被拦截;标志位表示相应内容存在内存中还是在磁盘中,如果CPU运行的时候发现标志位说内容在磁盘中会触发缺页中断)”达到映射效果;
一个子进程刚创建的时候,除了继承父进程的大部分的PCB之外还继承了父进程的进程地址空间和页表,因此同一个变量父子进程的虚拟地址是相同的,而在修改的时候,进程页表对应的物理地址会被系统修改,因此就在实际内存中有了自己的位置,此时虚拟地址不会受影响;这样出现一个地址两个不同的值就可以理解了~
进程地址空间定义:本质是内核的一个数据结构对象(struct mm_struct),该对象把总线可表示的存储空间(32位系统,2^32,4GB)按照规划划分为多个区域(long code_start ; long code_end)
理由:
- 让进程以统一的视角看待内存;
- 进程访问内存增加一个转化的过程,在这个过程中可以对寻址请求进行检查,一旦异常访问,直接拦截,不会影响物理内存,起到保护作用;
- 将进程管理和内存管理解耦;