一、为什么需要虚拟内存
要理解虚拟内存首先要明白为什么需要虚拟内存,物理内存就是我们通常所说的内存条。我们知道,相比于从硬盘中读写数据,直接读写内存的速度要快很多,但是内存的大小要比硬盘小很多。 要知道每个进程都有自己独立的4G内存空间,以16G的内存来说,如果我们在加载程序的时候直接给它分配4G大小的内存,那么像这种大小的程序,只要加载4个我们的内存就会被全部耗尽。然而,我们查看进程管理器可以发现系统中可能运行着几十个程序。为了解决这个问题,就有了虚拟内存的概念,它是利用磁盘空间虚拟出一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间。 当程序在运行时,它不会一次把所有的数据都加载到内存中,而是把它们放在交换空间(注意交换空间在磁盘上)。但是对于我们这个进程来说它以为自己获取到了一块完整的,连续的、足够大的内存空间,实际上这是虚拟的内存(4G)。真实的情况是一部分被放在真实的物理内存,还有一部分在磁盘(交换空间)。一个程序在运行的时候Linux会将其一部分暂时不用的数据放到交换区,以保证有充足的物理内存。如果在程序运行时,发现需要用的数据在磁盘,会先将它们读到内存。而当运行时发现内存不足了,又会将一部分数据写到磁盘(交换空间)。这里需要注意两点:
- 1、具体哪部分数据会被写入交换空间,这是由算法决定的。
- 2、Linux 系统会不时地进行页面交换操作,以尽可能保持多的空闲物理内存。
二、认识虚拟内存和物理内存的关系
前面说过,对于每个进程来说它都以为自己获取到了一块完整的,连续的、足够大的内存空间,实际上这是虚拟的内存,CPU操作的也是虚拟的地址,因此需要一个管家(MMU)来对虚拟地址进行翻译(虚拟地址转为物理地址),操作系统负责把虚拟地址和物理地址的映射关系维护在页表中。
注意:对于每个进程来说它以为自己拥有一块连续的内存(内存地址是连续的),但这块内存是虚拟的(内存的地址空间也是虚拟的),经过MMU(内存管理单元)转换后会将对应的虚拟地址转为物理地址,这些真实存在的物理地址可能是不连续的。
三、分页与虚拟内存的3种状态
1、什么是页?为什么要分页
将固定大小的内存看作是一页,并且每个页都有一个编号,通常Linux中每页大小为4KB;
那么为什么要分页呢?这是为了减少记录虚拟内存和物理内存的对应关系,想象一下,如果按照一个字节来对应,每一个字节的虚拟内存都对应一个字节的物理内存,就会有大量的记录。而以一个更大尺寸(页)来作为最小的单位来对应,那么这种映射关系就会减少很多。
2、虚拟内存的3种状态
虚拟内存中的虚拟页有以下3种状态:
- 未分配:虚拟内存对应的那一页并未被分配,不占用任何内存空间。
- 未缓存:虚拟内存对应的那一页被分配了,但在磁盘中,不在内存。
- 已缓存:虚拟内存对应的那一页就在内存中。
四、虚拟内存和物理内存映射
下图是一个简化的分页设计,为了弄清物理内存与虚拟内存的关系就不考虑复杂的多级分页了。
当CPU执行一个指令,遇到一个虚拟地址的时候,它需要获取这个虚拟地址对应的物理地址,这个工作由于MMU帮助完成:
- 1、当发现这个虚拟地址对应的页已缓存,就代表对应的真实空间已经处于内存中,返回这个虚拟地址对应的物理内存地址即可。
- 2、当发现这个虚拟地址对应的页处于未缓存状态,也就是对应的真实空间在磁盘中,系统将产生缺页中断,进程会被阻塞,等待系统将磁盘中的这一页数据复制到内存中,如果物理内存不足,系统会将合适的内存页复制到磁盘中,为新的内存页腾出位置,这个过程叫页面替换,然后再更新页表,重新建立虚拟内存到物理内存的映射关系。完成后,CPU接着导致缺页中断的执行继续执行。
五、总结
- Linux 系统会不时地进行页面交换操作,将暂时不用的页面交换到磁盘中,以便获得更多的空闲物理内存。
- 虚拟内存对于每个进程来说是一种假象,让每个进程都感觉自己拥有独立的、连续的内存空间。
- CPU在访问到虚拟地址时,需要把它翻译为实际物理内存地址。
- 页表维护了虚拟地址和真实地址的映射关系,并且记录了哪些内存上的数据在物理内存中,哪些在磁盘中。
- 缺页中断本质就是把进程需要的数据从磁盘上拷贝到物理内存中。