内存分配
在早期的计算机中,程序是直接运行在物理内存上,也就是说,程序运行时所访问的地址都是物理地址,如果计算机只运行一个程序且该程序所需的内存空间不超过物理内存大小,就不会有问题。现在计算机需要同时运行多个程序,那么如何将有限的物理内存分配给多个程序使用?
直接分配的弊端
假设一台计算机有64MB内存,程序A运行需要50MB,程序运行需要10MB,需要同时运行这两个程序,比较直接的做法就是将0MB~50MB分配给A,50MB~60MB分配给B。
这样的分配策略会带来很多问题:
- 地址空间不隔离 所有程序都直接访问物理地址,程序之间使用的地址空间共享物理内存,很容易发生恶意程序改写其他程序内存数据的情况;另外本身有bug的程序也有可能影响到其他程序的执行。这造成了程序运行不稳定的情况。
- 程序运行时地址不确定 在程序装入运行时,需要分配一块足够大的空闲区域,而这个位置不确定,那么在程序编写时,指令的跳转需要你自己计算得出绝对地址,这是十分麻烦的。
- 内存使用效率低 执行一个程序就将整个程序加载到内存,若需要继续同时执行另外的程序,则会出现内存不足,这时只能将内存中现有的数据换出到磁盘,磁盘、内存之间的大容量的换出换入必会导致效率低下
如何解决直接分配的弊端
解决地址空间不隔离和程序运行时地址不确定
从程序执行的角度看,我们不希望它介入到复杂得内存分配过程中,我们希望一个程序在执行的时候只需要一个简单得执行环境(独立单一的地址空间、单一的CPU,不用关心其他程序)。
可以把地址空间想象成一个很大的数组,数组大小取决于地址空间的地址长度,如64位的地址空间为2^64 = 18446744073709551616
,一般用十六进制表示0x0000000000000000~0xFFFFFFFFFFFFFFFF
地址空间分为物理地址空间和虚拟地址空间。
可以把物理地址空间想象成物理内存,它是实实在在存在,存在于计算机中。物理地址空间范围与设备的内存大小相关。
虚拟地址是指人们想象出来的并不存在的,每个进程都有自己独立的虚拟空间,且每个进程只能访问自己的地址空间。
使用虚拟地址空间和分段解决地址空间不隔离和程序运行时地址不确定
虚拟地址和物理地址通过映射函数来转换,通常由CPU转换,当程序访问超出虚拟地址空间的地址时,硬件会判断出非法访问并拒绝访问。由此,编写程序只需要在虚拟地址空间内即可(实质上是程序编写完才有一个虚拟地址空间)。
内存分段没有解决内存使用效率问题,因为换出数据到磁盘仍需要整页换出,为了解决这个问题,使用更细粒度的划分——分页
使用分页解决内存使用效率低
根据程序的局部性原理,当一个程序执行时,并不是所有数据都需要在一个时刻使用,因此可以按需将内容装载到内存中。
目前大部分操作系统都使用4KB大小的页。按照这个页大小将需要执行的程序虚拟内存空间分成多页,并把需要使用的页映射到内存,这就能使多个程序的分页同时装载到物理内存中,提高了内存使用效率。当程序执行到不在物理内存的分页时,就会出现页错误(Page Fault),然后操作系统将进程需要的页装载映射到物理内存。
页保护,每个页可以设置权限属性,谁可以修改、访问等,只有操作系统有权限修改这些属性。