文章目录
- 进程地址空间存在的原因
- 原因一
- 原因二
- 原因三
- 重新理解什么是挂起?
进程地址空间存在的原因
原因一
凡是非法访问或者映射,OS都会识别到,并终止该进程。
例子:
我们会发现我们定义的字符串常量只有只读权限,不能被我们更改。
为什么会这样呢?
因为页表具有读写权限,当一块地址只读的时候就会限制写的权限。
所以我们可以明白所有进程崩溃就是进程退出,也就是OS杀掉了该进程。
因为凡是非法访问或者映射,OS都会识别到,并终止该进程。所以有效的保护了物理内存。
地址空间和页表是OS创建并维护的!也意味着凡是想使用地址空间和页表进行映射,也一定要在OS的监管下来进程访问。
上面也便于保护物理内存中的所有合法数据也包括各个进程,以及内核的相关有效数据。
原因二
因为有地址空间和页表的存在,我们的物理内存中,是不是可以对未来的数据进行任意位置的加载?当然!
物理内存的分配和进程的管理两者之间可以做到没有任何关系。
这样内存管理模块VS进程管理模块就可以完成解耦合。
所以我们在C、C++语言上new,malloc空间的时候,本质是在哪里申请的呢?
本质是在虚拟地址空间上申请的。
如果我们申请了物理地址空间,但不立马使用是不是就造成了空间浪费?
是的!
例子说明:
小明向妈妈拿了200块钱准备第二天去玩具城买玩具,妈妈同意了。小明高高兴兴的去睡觉,等待第二天的到来。这时二叔来了,二叔准备借200块钱他有急用,二叔说明天上午就还回来。于是妈妈就同意了,准备先把钱借给二叔,因为二叔有急用,我们现在并用不到这些钱。
从上面我们可以类比:
钱:物理地址
小明:进程1
二叔:进程2
妈妈:OS(操作系统)
本质上因为有地址空间的存在,所以上层申请空间,其实是在进程地址空间上申请,物理内存甚至可以一个字节都不给!!
而真正进程准备进行对物理地址空间访问的时候,OS才会执行内存的相关管理算法,帮助该进程申请内存,构建页表映射关系,然后再让你进程内存访问。
上面的过程是由OS自动完成,用户包括进程完全零感知。
例子:
小明向妈妈拿了200块钱准备第二天去玩具城买玩具,妈妈同意了。小明高高兴兴的去睡觉,等待第二天的到来。半夜二叔来了,小明睡着了。二叔向妈妈借200块钱他有急用,二叔说明天上午就还回来。于是妈妈就同意了,先把钱借给二叔,因为二叔有急用,第二天上午二叔早早的就把200块钱还回来,小明还没睡醒。等小明睡醒了妈妈给了200块钱让小明自己去买。而二叔借钱的事情小明完成不知情。
延迟分配的策略,提高了整机的效率,内存的使用率几乎是百分百。
原因三
因为在物理内存中理论上可以任意位置的加载进程内容和数据,所以物理内存中几乎所有的数据和代码在物理内存中都是乱序的。
但是因为页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,那么在进程的视角中所有的内存分布都是有序的!
所以我们可以得出结论:地址空间和页表的存在可以将内存分布有序化!
进程地址空间是OS给进程画的大饼,那么为什么要有进程地址空间?
进程要访问物理内存中的数据和代码,可能目前并没有在物理内存中,同样的也可以让不同的进程映射到不同的而物理内存中。这样子很容易做到进程独立性的实现!
所以进程的独立性可以通过地址空间+页表的方式实现。
因为有地址空间的存在,每个进程都认为自己拥有4GB空间,并且各个区域是有序的,进而可以通过页表映射到不同的区域,来实现进程的独立性。
每个进程并不需要知道彼此之间的存在。
重新理解什么是挂起?
加载的本质就是创建进程,那么是必须非得把所有的程序代码和数据加载到内存中,并创建内核数据结构建立映射关系?并不是的。
==在最极端的情况下,甚至只有内核数据结构被创建了出来。==也就是新建状态,理论上可以实现队程序的分批加载!
既然可以分批加载也可以分批换出,甚至进程短时间不会再被执行,比如阻塞了!进程的数据和代码就会被换出。
页表映射的时候,不仅仅可以映射到物理内存,磁盘中的位置也可以映射。