进程地址空间
C/C++对空间的划分
- 这个图中展示的内容是在内存吗? > 我们称这个区域为地址空间。
图示
#include<stdio.h>#include<stdlib.h>int val_1;//未初始化全局变量int val_2=100;//已初始化全局变量intmain(){printf("code address: %p\n",main);//代码区constchar*str="hello world";//字符常量区printf("str address: %p\n",str);//为什么是str,str就是保存的字符串常量的起始地址。printf("val_2 address: %p\n",&val_2);printf("val_1 address: %p\n",&val_1);char* mem=(char*)malloc(100);//堆区printf("heap address: %p\n",mem);printf("stack address: %p\n",&str);//栈区,局部变量在栈区}
- 栈区的地址是向下增长,堆区的地址是向上增长。堆栈相对而升。
- static 修饰的变量> 为什么用 static 修饰的变量,就不会随着函数调用而被释放?> 因为 static 修饰的局部变量,在编译时已经被编译到全局数据区了,所以才不会被释放。
- 结论> 如果变量的地址是物理地址,不可能存在父子进程访问的变量地址相同但是值不同的现象。出现这样的现象称为线性地址或者虚拟地址。所以平时写得 C/C++用的指针,指针里面的地址,绝对都不是物理地址,而是称为虚拟地址。
地址空间
一个可执行程序有代码和数据两部分,还会创建进程地址空间。每一个进程都有进程地址空间。
地址空间的概念
概念:所谓的进程地址空间,本质是一个描述进程可视范围的大小。地址空间内一定要存在各种区域划分,对线性地址进行 start 和 end 划分即可。地址空间本质是内核的一个数据结构对象,类似 PCB 一样,地址空间也是要被操作系统管理的:先描述,再组织。
主要组成部分
- 页表 K(键) V(值) 结构 键值为虚拟地址,值为物理地址> 当程序运行时,就会根据页表中的虚拟地址来找到对应的物理地址。
- 写时拷贝的本质:> 重新开辟空间,但是在这个过程中,左侧的虚拟地址是不变的,不影响虚拟地址;而是在物理内存中开辟了新空间,将更改的信息存储到这个空间中。所以就出现了虚拟地址相同而值不同的现象。
常见细节问题
- 总线> CPU 与内存之间的线称为:系统总线> 内存和外设之间的线称为:IO 总线> 在 32 为计算机中,有 32 位的地址和数据总线。每一根总线只有 0,1 的概念。一共有 2^32 种状态。2^32*1byte=4GB,所以一个 32 位系统最多能装载 4GB 的内存空间。
- 地址空间究竟是什么?- 什么叫做地址空间?> 地址总线排列组合形成的地址范围[0,2^32),- 如何理解地址空间上的区域划分?
- 为什么要有进程地址空间?> > - 1. 让所有进程以统一的视角看待内存结构> - 1. 增加进程虚拟地址空间,可以让我们访问的内存的时候,增加一个转换的过程,在这个转换的过程中,可以对我们的寻址请求进行审查,所以一旦异常访问,我们直接拦截,该请求不会到达物理内存,保护物理内存。> - 1. 因为有地址空间和页表的存在,可以将进程管理模块和内存管理模块进行解耦合。
- 页表结构是一个映射表。cr3 寄存器保存的是页表的起始地址(物理地址),这个地址是物理地址。
- 怎么知道进程的代码数据在不在内存?> 操作系统对大文件可以实现分批加载
- 惰性加载> 操作系统对进程的加载策略为惰性加载的方式,
- 缺页中断> 在页表中有一列为对应的代码和数据是否已经被加载到内存,如果显示不在,操作系统会触发缺页中断,操作系统会重新申请内存。(写时拷贝就是一种缺页中断)
- 进程被创建的时候,是先创建内核数据结构呢?还是先加载对应的可执行程序呢?> 先创建内核数据结构。
版权归原作者 Mike! 所有, 如有侵权,请联系我们删除。