物理内存与虚拟内存的区别与联系-这一篇就够了

背景

操作系统有虚拟内存与物理内存之分。在虚拟内存出现之前,程序寻址用的都是物理地址,因此程序能寻址的范围是有限的,具体程序可以寻址的范围有多大取决于CPU的地址线条数。比如在32位平台下,寻址的范围是2^32也就是4G,并且这是固定的,如果没有虚拟内存,且每次开启一个进程都分配出来4G的物理内存,就会出现很多问题:

  • 因为物理内存是有限的,当有多个进程要执行的时候,对每个进程都要分配4G内存,很显然你内存若小一点,这很快就分配完了,于是没有得到分配资源的进程就只能等待。当一个进程执行完后,再将等待的进程装入内存。这种频繁的装入内存的操作是很没效率的。
  • 由于指令都是直接访问物理内存的,那么进程就可以修改其他进程的数据,甚至会修改内核地址空间的数据,这是我们不想看到的。
  • 因为内存是随机分配的,所以程序运行的地址也是不正确的。

为了解决上述问题,于是就出现了虚拟内存。

虚拟内存原理及与物理内存的关系

一个进程运行时会被分配4G的虚拟内存。进程有了虚拟内存后,每个进程都认为自己拥有4G的内存空间,当然这只是每个进程认为的。但实际上,虚拟内存对应的实际物理内存,可能只对应的分配了一点点的物理内存,实际使用了多少内存,就会对应多少物理内存。

进程得到的这4G虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它的数据是存储在多个物理内存碎片的,还有一部分存储在外部磁盘存储器上,在需要时将数据交换进物理内存。

进程开始要访问一个地址,它可能会经历下面的过程

  1. 进程每次要访问地址空间上的某一个地址时,都需要把地址翻译为实际物理内存地址。
  2. 所有进程共享一整块物理内存,每个进程只把自己目前需要访问的虚拟地址空间映射到物理内存上。
  3. 进程需要知道哪些虚拟内存地址空间上的数据在物理内存上,哪些不在(可能这部分存储在磁盘上),若在物理内存上存在,则需要进一步知道数据存储在物理内存上的具体位置,这都需要通过页表来记录。
  4. 页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)。
  5. 当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常。
  6. 缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。

关于虚拟内存与物理内存的联系,下面这张图可以帮助我们巩固。


物理内存与虚拟内存的区别与联系-这一篇就够了_第1张图片
物理内存与虚拟内存的关系

页表的工作原理如下图


物理内存与虚拟内存的区别与联系-这一篇就够了_第2张图片
页表工作原理
  1. cpu想访问虚拟地址所在的虚拟页(VP3),根据页表,找出页表中对应的页表项,判断有效位。
  2. 若有效位为1,则DRMA缓存命中,根据物理页号,找到物理页当中的内容,返回。
  3. 若有效位为0,则发生缺页异常,调用内核缺页异常处理程序。内核通过页面置换算法选择一个页面作为被覆盖的页面,将该页的内容刷新到磁盘空间当中。然后把VP3映射的磁盘文件内容缓存到该物理地址对应的页上面。然后将页表项中的有效位变成1,第二部分存储了对应的物理内存也的地址。
  4. 缺页异常处理完毕后,返回中断前的指令,重新执行,此时缓存命中,执行1。
  5. 将找到的内容映射到高速缓存当中,CPU从高速缓存中获取该值,结束。

总结

当每个进程创建的时候,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射关系(叫做存储器映射)。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去访问页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。

另外在进程运行过程中,通过malloc来动态分配内存时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

可以认为虚拟空间都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上,通过mmap,mmap是用来建立虚拟空间和磁盘空间的映射关系的)

价值

既然每个进程的内存空间都是一致而且固定的(32位平台下都是4G),所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际内存地址,这交给内核来完成实际映射关系。
当不同的进程使用同一段代码时,比如库文件的代码,在物理内存中可以只存储一份这样的代码,不同进程只要将自己的虚拟内存映射过去就好了,这样可以节省物理内存。
在程序需要分配连续空间的时候,只需要在虚拟内存分配连续空间,由操作系统自动完成连续的虚拟物理内存地址到分散的物理内存地址的映射,这样就可以有效的利用碎片化的物理内存。

参考与借鉴

https://blog.csdn.net/lvyibin890/article/details/82217193

你可能感兴趣的:(物理内存与虚拟内存的区别与联系-这一篇就够了)