早期没有虚拟内存的时候,是直接在内存中为程序分割内存。这样就会带来一些问题
进程地址空间不隔离:由于程序是直接访问内存的,在多进程的情况下,那么一些恶意程序就可以随意的修改其他程序的数据,这显然是不愿意看到的。甚至一些非恶意程序在使用不恰当的情况下也可能会导致其他程序的数据发生改变,我们希望移除程序出错不会影响到其他程序。
内存使用效率低:比如内存大小128MB,而我们的A程序和B程序已经占用了110MB,此时大小为20MB的C程序需要运行,此时内存的空闲空间是不够用的,那么就会将正在使用的程序中的占用的内存的部分数据拷到磁盘中存储,为C程序挪出足够的空间。这其中就涉及到了大量数据的拷进拷出,极大的影响效率
程序运行的地址不确定:此时C的内存很有可能是随机分配的(这里没有说是A和B哦),所以导致C的运行地址我们是不确定的
为了解决这个问题呢?
提出了一种中间隔离的想法,也就是说添加一个中间层间接的去解决访问物理地址。按照这种方法,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址。那如何去实现呢,通过MMU来映射(映射 f(x)=y)。对于每一个进程而言,我们只需要考虑其自己对应的虚拟地址即可,操作系统可以自动帮我们对应上相应的物理地址,操作系统完成映射关系,映射不同程序在不同的区域,实现了进程地址空间隔离。
分段
人们想到在虚拟地址空间和物理地址空间之间做一一映射。 操作系统保证不同进程的地址空间被映射到物理地址空间中不同的区域上,如此就能形成地址的隔离。
假设有两个进程A和B,进程A所需内存大小为10M,其虚拟地址空间分布在0x00000000到0x00A00000,进程B所需内存为100M,其虚拟地址空间分布为0x00000000到0x06400000。那么按照分段的映射方法,进程A在物理内存上映射区域为0x00100000到0x00B00000,,进程B在物理内存上映射区域为0x00C00000到0x07000000。于是进程A和进程B分别被映射到了不同的内存区间,彼此互不重叠,实现了地址隔离。
(简要理解:分段式将物理内存根据进程的虚拟地址空间来完成分段,保证不同进程所对应不同的物理内存段,而且我们在程序中操作的是虚拟地址空间,而每一个进程的虚拟地址空间所映射的物理段是固定的所以不用担心修改其他程序的数据)
注意:在每个应用程序的虚拟地址空间里,起始地址都是从0开始,这样仿佛每一个应用程序都是独占所有的内存空间。其实在物理内存中不是,物理内存是分段处理的
分页
在分段的方法中,每次程序运行时总是把程序全部装入内存,而分页的方法则有所不同。分页的思想是程序运行时用到哪页就为哪页分配内存,没用到的页暂时保留在硬盘上。当用到这些页时再在物理地址空间中为这些页分配内存,然后建立虚拟地址空间中的页和刚分配的物理内存页间的映射。
一个可执行文件(PE文件)其实就是一些编译链接好的数据和指令的集合,它也会被分成很多页,在PE文件执行的过程中,它往内存中装载的单位就是页。当一个PE文件被执行时,操作系统会先为该程序创建一个4GB的进程虚拟地址空间。
创建4GB虚拟地址空间其实并不是要真的创建空间,只是要创建那种映射机制所需要的数据结构而已,这种数据结构就是页目和页表。
分页方法的核心思想就是当可执行文件执行到第x页时,就为第x页分配一个内存页y,然后再将这个内存页添加到进程虚拟地址空间的映射表中,这个映射表就相当于一个y=f(x)函数。应用程序通过这个映射表就可以访问到x页关联的y页了。
4G的来源
我们都说虚拟地址空间的大小为 0~4G,为什么是0~4G呢。因为在32位平台下,指针的长度为4,所以指针的寻址范围:0x00000000-0xFFFFFFFF 正好是 0~4G的容量。
详细介绍虚拟地址空间
堆栈模型、解决程序加载内存
Linux每一个运行的程序,操作系统都会为其分配一个虚拟地址空间。
这个我已经说烂了,不想说了