内存物理程序是什么(CPU眼里的:虚拟内存)

心碎小饭包 电脑 26

“虚拟内存是现代计算机、操作系统的基石,但为什么虚拟内存在编程中,毫无存在感?为什么也有人:虚拟内存就是一个骗子?”

人人都听说过虚拟内存,人人似乎都没有用过虚拟内存。为什么说虚拟内存是现代操作系统过的基石?没有虚拟内存的世界,会是什么样子?为什么苹果最近才打开SWAP功能?让我们用CPU的视角,去解答所有的问题。

01

提出问题

请问你的手机、电脑上的内存有大?4GB、8GB、还是:128GB?你手机上的APP,知道手机有这么大的内存吗?

实际上,无论是“王者荣耀”APP,还是你的 Hello World 程序,它们都认为你的内存大约有:40亿个4GB 这么大。

在我们学习C/C 编程时,我们很少提及:虚拟内存。但忽视虚拟内存的存在,难免会对程序和操作系统的行为产生一些误解。让我们一起讨论一下这个最强大、最无感的技术:虚拟内存。

02

虚拟内存原理

先简单介绍一下:虚拟内存的工作原理。虚拟内存,就是在物理内存的基础上,为每一个进程营造一个更加庞大的内存:

这个虚拟内存的空间大小,由CPU的位数决定,也就是CPU能寻址多少位,虚拟内存就有多大。32位的CPU,虚拟内存的空间是:4GB;64位的CPU,虚拟内存的空间就是:4G * 4GB。

从此以后,CPU都会在这个虚拟内存中,进行读、写操作。但毕竟是虚拟的,真实数据最终还是要存储在实际的物理内存上。所以,还需要内存管理单元MMU和操作系统一起合作,实现虚拟内存到物理内存之间的映射:

由于有硬件加持,这种映射对程序员是无感的,也不影响CPU的运行效率。这就是虚拟内存,大概的工作原理。

03

减少内存碎片

那使用虚拟内存有什么好处呢?第一个好处是:减少内存碎片。例如,下面是一块物理内存,一共有连续的12KB(假设相互临近的两个内存颗粒,它们的内存空间是连续的)。程序APP 1需要用到4KB内存,如果它成功申请到了中间的4KB内存后,剩下的空闲内存总量就还剩8KB:

如果此时,还想运行正好需要8KB内存的APP 2就不行了!因为,虽然从总数上看还有8KB的空余内存,但它们不是连续的,而程序运行,或在malloc或new的时候,往往需要的是连续内存。

如果使用虚拟内存的话,这个问题就很容易解决了:

如你所见:尽管所剩的物理内存还是支离破碎的4KB 4KB内存;但通过页表,我们可以对上、下两个不连续的物理内存,进行重新映射,让它们在虚拟内存空间上是连续的。

由于程序运行时,只能看见虚拟内存,所以程序并不会感知到这两块4KB的物理内存实际上是不连续的。从而避免了因为内存碎片,所造成的内存浪费。

04

简化运行条件

这就为程序加载,提供了极大的灵活性。让程序员不必在编程的时候,考虑如何让程序去适配计算机的内存环境。

05

隔离进程

第三个好处是:隔离进程。还是这个单进程的a.out程序,我们第一次运行的时候,操作系统为a.out构建了一个虚拟内存空间;如果再运行一次,操作系统就为a.out再构建一个虚拟内存空间:

虽然从虚拟内存上看,两个a.out所在的内存空间是完全一致的!但它们实际上被映射在不同的物理内存上,所以,两个a.out进程是完全隔离的。

即使进程A遇到错误而崩溃,也不会牵连到进程B。同时,进程A想非法探测进程B,或其他进程的内存,也是不可能的。由于有MMU的保护,进程A只能访问MMU已经为其映射好的物理内存。这在一定程度上,提高了系统的安全性。

06

内存共享

第四个好处是:有效的利用内存资源。例如:我们的电脑、手机上,会预存很多字体文件。几乎所有的APP都会使用这些字体资源,因为这些资源多为只读类型,所以就没有必要,为每个程序都复制一份资源,而是直接内存共享。

例如,系统在开机时,操作系统由于也需要使用字体文件,所以,会加载一份到内存中。此后,如果其他APP(例如:APP-1、APP-2)也需要使用到字体文件,它们就不需要再把字体文件加载到自己的内存空间里面了。

相反,它可以通过内存映射,直接共享刚才操作系统已经加载好的字体文件:

但如果需要改写共享数据的话,例如:修改一下现有字体,为了避免影响其他程序。就必须自己拷贝一份字体了,这也叫:copy on write。

07

SWAP

第五个好处是:以小博大。也就是大家常说的:SWAP,假设这是当前的计算机系统,如你所见,我们的程序已经用尽了全部的物理内存:

简单起见,我们通过设置页表,让虚拟内存页和物理内存页,一一对应。上面是虚拟内存页的编号;下面是对应的物理内存页的编号。此时,如果程序还需要申请一个内存页,应该如何处理呢?

为了让程序继续运行下去,操作系统会通过一定的算法,选择将某一个物理内存页(这里,选择的是1号物理内存页)暂时转移到硬盘上:

同时更新一下对应的页表信息,记录一下内存页所在的硬盘扇区号码H-100,从此,1号虚拟内存页,就对应着H-100硬盘扇区了。这样就临时腾出了一个空闲的内存页,更新一下页表:

从此,5号虚拟内存页,就对应着1号物理内存页了。如此完成内存映射后,就可以返回给程序使用了。

那如果程序需要读取刚才转移到硬盘上的1号虚拟内存页,那该怎么办呢?显然,这时仍然没有空闲的物理内存可供使用,还是老办法,操作系统再选择一个物理内存页(这里选择的是2号物理内存页)将它也暂时转移到硬盘上面,并更新一下页表,从此,2号虚拟内存页,就对应着H-200硬盘扇区了:

这样就又临时腾出了一个空闲的物理内存页。

此时操作系统,就可以根据1号虚拟内存页对应的硬盘扇区号码H-100,把存在硬盘上的内存页读取到刚刚空闲2号物理内存页上:

当然,由于1号虚拟内存页对应的物理内存页改变了位置,所以,对应的物理页号码就从H-100变为了:2号物理内存页。

不得不说,这是一个伟大的功能,但也是最为大家诟病的功能之一。因为,一旦系统内存窘迫到需要SWAP的时候。如你所见,内存和硬盘之间的数据,就会频繁交换,以当时机械硬盘的速度,系统效率立刻被拖慢1000倍!

如此差的体验,还不如不用!直到今天,固态硬盘在效率上取得了巨大进步后,苹果才在最新的iPad系列产品上,打开了SWAP功能。

08

总结

2. 虚拟内存一方面简化了应用程序的开发过程,让程序员无需关心与软件功能没有直接关系的信息,例如:目标机的内存环境;它也能充分的利用计算机的内存资源、硬盘资源,还实现了进程间的安全隔离;但另一方面,它也增加了操作系统的开发难度、学习成本和CPU的硬件成本。

3. 不是所有的计算机系统都需要虚拟内存,例如:STM32单片机和嵌入式环境,它们很多是不支持虚拟内存的,甚至Linux也有无虚拟内存的版本:ucLinux。

09

热点问题

Q1:我是在校的学生,在学习虚拟内存的时候,感觉非常难以理解,有什么办法解决吗?

需要特别提醒一点的是:虚拟内存技术是操作系统 MMU的软、硬结合体,本章节描述的所有内容,都不能仅靠其中的一方来实现。

Q2:有没有关于实现虚拟内存的软件代码,可以参考、学习的?

A2:Linux内核代码是比较好的学习资料,市场上的相关书籍也比较丰富。但它们往往讲述x86 CPU在虚拟内存上的实现方法,由于历史原因,x86 CPU在操作MMU上面,会有些繁琐,相比之下,ARM CPU在虚拟内存的实现上,会更加直观一点。

Q3:虚拟内存这么强大,为什么系统还会死机?

同时,调试内核、驱动程序,往往也是程序开发的至暗时刻。其调试环境甚至不如单片机的调试环境。普通开发者很难获得价格高昂的调试设备,往往只能依靠原始的打印输出,来进行软件调试。

10

参考视频链接

标签: 虚拟 内存页 物理

抱歉,评论功能暂时关闭!