虚拟内存空间、虚拟地址空间和MMU映射(内存分页机制)

其实,这两者之前几乎没有什么关系。

虚拟内存空间:

在计算机早期,程序都是比较小的,所以128M的内存能够装下整个程序,进而执行(程序必须放在内存中才能被CPU执行)。然而,随着计算机的发展,程序增长的速度超越了内存增大的速度,因此出现了一个2-3G的程序而只有1G内存的现状。这时,如果要运行这个大程序,按照原来的想法就行不通了,因为整个内存才1G,而程序有2-3G的大小,远远超出了内存的容量,因此CPU就不能够执行这个程序。

为解决这个问题,人们就想出了虚拟内存空间的办法。由于内存容量不够大,而磁盘(也称硬盘)的容量很大,所以干脆就把磁盘的一部分拿出来作为内存交换区或缓冲区等。这样,当运行一个大于内存容量的程序的时候,可以把该程序暂时没有被执行到的代码和数据放到硬盘的缓冲区或交换区中,而把要执行的代码和数据放到真正的内存中去,这样就解决了上面的问题。所以可以简单的认为,内存和硬盘上的缓冲区或交换区组成了虚拟内存,它欺骗了系统,让系统以为整个程序都被放到了内存中。

 

虚拟地址空间:

计算机有了进程的概念后,就需要让每个进程占用一定的内存空间(因为内核会快速切换进程,以保证多任务,即让人们错以为同一时刻系统运行了很多程序)。但是,内存空间是有限的,而计算机的进程可以无限多,如何让每个进程之间能够相对独立呢?这样就出现了虚拟地址空间。

32位的内核让每个进程都拥有一个4G大小的虚拟地址空间,其中0-3G的空间为用户空间,用户代码可以直接访问,而3G-4G的虚拟空间为内核空间,用户不能直接访问,只能通过中断或系统调用进入内核空间。MMU(内存管理单元)会把虚拟内存地址映射到真实的物理地址。这样程序中可以随意的在这4G虚拟地址空间中进行操作,而具体占用了哪个真实的物理地址则不必关心,使得开发者能够专注于解决问题而不是操作内存。同时,这样也保证了一个进程不会占用另一个进程的物理空间(因为映射物理空间是由MMU来完成的,它知道哪些物理空间可以用,哪些是已经被占用的),提高了系统安全性。

 

所以,从以上解释可以看出,虚拟地址空间和虚拟内存空间几乎没有关系。

 

MMU映射和内存分页机制

MMU负责将虚拟地址空间映射到具体的真实物理地址空间,那么它是如何完成的呢?

当一个进程请求MMU提供内存映射时,会给出一个32位的值(可以认为是虚拟地址空间),这32位构成了MMU的线性地址空间。MMU的线性地址空间分为页目录索引(占10位)、页表索引(占10位)和偏移量(占12位)。页目录是一个指向页表的指针数组,页表是一个指向内存页面的指针数组,所以一个页目录就能够确定一个页表,继而确定一个内存页面,然后根据偏移量得到这个页面中的某个物理地址,这就完成了从虚拟地址空间映射到真实物理地址空间。也就是说,当有一个进程有内存地址访问时,MMU会根据访问地址的最高10位(即页目录索引)中找到对应的页表基地址,再从次高10位(即页表索引)中找到对应的页首基地址,然后根据最后12位(即偏移量)与页首基地址找到逻辑地址对应的真正的物理地址。

在内核层面,每个虚拟地址(逻辑地址)和物理地址(真实地址)都被划分为固定大小的页面(内存分页),每个合法的逻辑页面都处于一个真实页面中,方便MMU进行转换。