导读 | 当多个程序同时运行时,如何让多个应用程序共同使用物理内存资源? |
如何管理内存?
当多个程序同时运行时,如何让多个应用程序共同使用物理内存资源?有两种简单的方法:
使某一个应用程序独占所有的内存资源。一个程序要运行就将另一个程序的内存数据转存到硬盘中,使要运行的程序独占所有内存资源。
缺点:硬盘读写速度很慢(相对而言),如果应用程序频繁切换,必然会造成大量时间开销。
让每一个应用程序独占一部分内存资源。多个应用程序的数据,可以一直保存在内存中,避免了硬盘读写的时间开销。
缺点:无法保证不同应用程序内存之间的隔离性,如果应用程序A错误读取或修改了应用程序B的内存数据,就会造成严重的后果。
缺点:无法保证每个应用程序分到的内存地址是连续的,这会增加程序编写的复杂度。
虚拟内存
为了使不同的应用程序高效,安全的使用内存,虚拟内存应运而生。顾名思义,虚拟内存是对物理内存的一种抽象,它介于应用程序与物理内存之间。应用程序是面向虚拟内存编写的,而不再是面向物理内存编写的。应用程序在运行时只能使用虚拟地址CPU负责将虚拟地址翻译成物理地址,操作系统负责设置虚拟地址到物理地址的映射。
优点:
应用程序只能看到自己的虚拟地址空间,从而保证了应用程序之间内存的隔离性,使应用程序运行更安全。
每个应用程序的虚拟内存空间是连续的、统一的,从而降低了编程的复杂性。
地址翻译:CPU中的内存管理单元(MMU)负责将虚拟内存翻译成物理内存。为了加速地址翻译过程,MMU中还加入了转址旁路缓存(TLB),TLB可以缓存一部分虚拟地址到物理地址的映射,从而加速翻译。
虚拟内存是硬件提供的一种功能,而不是操作系统提供的。
分段和分页
分段和分页是MMU将虚拟地址翻译为物理地址的两种机制。
分段
在分段的机制下,操作系统以“段”(一段连续的物理内存)的形式来管理、分配虚拟内存和物理内存。应用程序的虚拟地址空间由若干个不同大小的段组成,比如:代码段、数据段。
每一段都是一个虚拟地址空间,虚拟地址由两部分组成:段号和段内地址。MMU通过段表找到对应的物理段,再通过虚拟地址中的段内地址(偏移量)找到对应的物理地址。
缺点:造成物理出现外部碎片。虚拟地址中相邻的段对应到物理内存中可能就不相邻了,这样虽然实现了物理内存的离散分配,但是可能造成物理内存中相邻的段之间出现内存碎片(不足以映射给虚拟内存中的段)。
分页
将应用程序的虚拟地址空间划分成连续的,等长的虚拟页,同时物理内存也被划分为等长的,连续的虚拟页。操作系统为每一个应用程序构造页表(虚拟页到物理页的映射关系表)。分页机制下的虚拟地址由两部分构成:虚拟页号和页内偏移量。
MMU通过应用程序的页表找到虚拟页号对应的物理地址,再通过页内偏移量找到对应的物理地址。
多级页表
上面的简单分页机制在位操作系统中,还能用,但是在64位操作系统中就不行了。64位操作系统,虚拟地址长64位,也就是8个字节,64位寻址范围是0~2^64,如果一个虚拟页大小4KB,那么就会有2^64/4KB个虚拟页,如果用一张页表来记录,一个页表项8字节,那么页表大小就为2^64/4KB*8,这是一个无法接受的数字。为了压缩页表的大小,多级页表应运而生。下面是一个4级页表的示意图:
在多级分页机制下,0级页表只有一个页表页,而其余每一级页表都可以拥有多个离散的页表页。虚拟地址可以分为两部分:虚拟页号和页内偏移。只不过虚拟页号可以进一步分为四个部分,依次对应其在对应级数页表中的索引。当任意一级页表中的条目为空时,就意味着该条目对应的下一级页表不需要存在,因此节省了大量空间。Linux就该这么学