链接:
虚拟内存的由来:
在早期的计算机中,是没有虚拟内存的概念的。我们要运行一个程序,会把程序全部装入内存,然后运行。
当运行多个程序时,经常会出现以下问题:
1.进程地址空间不隔离,没有权限保护。
由于程序都是直接访问物理内存,所以一个进程可以修改其他进程的内存数据,
甚至修改内核地址空间中的数据。
2.内存使用效率低
当内存空间不足时,要将其他程序暂时拷贝到硬盘,然后将新的程序装入内存运行。
由于大量的数据装入装出,内存使用效率会十分低下。
3.程序运行的地址不确定
因为内存地址是随机分配的,所以程序运行的地址也是不确定的。
我们都知道,每个程序被运行起来后,它将拥有自己独立的线性空间,这个线性空间由cpu的位数决定,比如32位硬件平台决定了线性地址空间的寻 址空间为02^32-1,即0x000000000xFFFFFFFF,也就是4GB大小。如果每个进程都有4GB,那我们的内存根本就不够用。其实,这4GB并不是真正的内存空间,它是虚拟内存空间,顾名思义,之所以称为虚拟内存,是和系统中的物理内存相对而言的,它是操作系统内核为了对进程地址空间进行管理而设计的一个逻辑意义上的内存空间概念。我们程序中的指针其实都是这个虚拟内存空间中的地址。虚拟内存是将系统硬盘空间和系统实际内存联合在一起供进程使用,给进程提供了一个比内存大的多的虚拟空间。在程序运行时,只需要把虚拟内存空间的一小部分通过页映射模式,经过MMU部件转换映射到物理内存即可。
那么这4GB的空间是不是就由该进程随意使用呢?遗憾的是,不可以。因为操作系统需要监控进程,进程只能使用那些操作系统分配给它们的地址,如果访问未经允许的空间,那么操作系统会捕获到这些访问,将进程的这种访问当作非法操作,强制结束进程。Linux操作系统将进程的4G虚拟空间分配为1:3的结构,1G是操作系统本身,剩下的3G是留给进程使用的。所以在一个进程的眼里,只有操作系统和它自己,就像整个计算机都被它“承包”了。
虚拟内存的重要性
1.CPU操作的数据一般来自于寄存器或者内存,磁盘上的数据是不能直接操控的。而对于内存而言,存放的数据五花八门,形如“HelloWorld.c”的一段程序,除了运行时,他在不在内存对于计算机运行而言没什么所谓。但是形如操作系统尤其是操作系统内核,他必须常驻内存,而且我们需要对他所在的内存加以保护,否则随便一个程序就能修改它所在的内存,对于系统而言非常不安全。
因此我们不能把物理地址直接暴露给程序,而是利用虚拟地址。
2.程序大于内存的状况经常发生,我们需要一种技术能使得在程序大于内存的情况仍能运行该程序。显然此时又是需要到虚拟内存了。使用虚拟内存的计算机,将程序分为固定大小的页。当执行它时,不需要将所有的页都放进内存中,只需要放进当前执行的代码所在的页。
虚拟内存的存在形式
虚拟内存是存在磁盘中的连续的 字节大小的单元组成的一个数组。
2.分页
上述提到的虚拟内存是存在于磁盘中的,虚拟内存会被切分为大小一致的页形式,即虚拟页面(Virtual Page,VP)。同样的,物理内存也被分为大小一致的页形式,称为物理页(Physical Page,PP)虚拟页面和物理页面的大小是相同的,所以我们才可以将虚拟页面映射为物理页面。如下图所示:
3.页表
我们已经知道了进程是存在于虚拟内存中的,然后将其一页一页地映射到物理内存上。那么计算机怎么知道哪一页的虚拟页面映射哪一页的物理页呢?那就是利用页表。
上图是一个页表的基本形式(实际上有更多的位提供更多的功能,这里不赘述)他有一个有效位和n位地址字段构成物理内存的区域组成。每一行可视为一个页表项,将虚拟地址翻译为物理地址需要用到一个页表项。为了使cpu可以简单快速的翻译虚拟地址,一般页表存放在一段连续的内存空间中。
页表常驻内存,当cpu访问到有效位为1的页表项时将其送到cpu内部,经翻译得到物理地址。如果访问到有效位为0的情况,会引起一个缺页中断。
4.虚拟内存到物理内存的映射过程
此时先记住一个电子器件内存管理单元(MMU,Memory Managament Unit),他是负责地址翻译的器件。在CPU中。
地址翻译的大致流程如下: