本系列博客是《程序员的自我修养–链接、装载与库》的学习笔记,本文是书中1.5部分
在前文我们讲到了进程的概念,进程的总体目标是希望每个进程从逻辑上来看都可以独占计算机的资源。
操作系统的多任务功能使得CPU能够在多个进程之间很好地共享,从进程的角度看好像是它独占了CPU而不用考虑与其他进程分享CPU的事情。
操作系统的I/O抽象模型也很好地实现了I/O设备的共享和抽象,那么唯一剩下的就是主存,也就是内存的分配问题了。
早期计算机的程序访问的地址都是物理地址,如果一个计算机同时只运行一个程序,那么只要程序的内存空间不超过物理内存的大小就不会出现内存问题。但是事实上,我们必须同时运行多个程序,这样会使CPU利用率比较高。那么很明显的一个问题是,如何将计算机上有限的物理内存分配给多个程序使用。
解决内存分配的思路是:增加中间层
即使用一种间接的地址访问方法。整个想法是这样的:
我们把程序给出的地址看作是一种虚拟地址(Virtual Address),然后通过某些映射的方法,将这个虚拟地址转换成实际的物理地址。这样,只要我们能够妥善地控制这个虚拟地址到物理地址的映射过程,就可以保证任意一个程序所能够访问的物理内存区域跟另外一个程序相互不重叠,以达到地址空间隔离的效果。
让我们回到程序的运行本质上来。用户程序在运行时不希望介入到这些复杂的存储器管理过程中,作为普通的程序,它需要的是一个简单的执行环境,有一个单一的地址空间、有自己的CPU,好像整个程序占有整个计算机而不用关心其他的程序(当然程序间通信的部分除外,因为这是程序主动要求跟其他程序通信和联系)。
所谓的地址空间是个比较抽象的概念,你可以把它想象成一个很大的数组,每个数组的元素是一个
字节,而这个数组大小由地址空间的地址长度决定,比如32位的地址空间的大小为 2^32 = 4 294 967 296 字节,即4GB,地址空间有效的地址是0~4 294 967 295,用十六进制表示就是0x00000000~0xFFFFFFFF。地址空间分两种:虚拟地址空间(Virtual Address Space)和物理地址空间(Physical Address Space)。物理地址空间是实实在在存在的,存在于计算机中,而且对于每一台计算机来说只有唯一的一个,你可以把物理空间想象成物理内存。
虚拟地址空间是指虚拟的、人们想象出来的地址空间,其实它并不存在,每个进程都有自己独立的虚拟空间,而且每个进程只能访问自己的地址空间,这样就有效地做到了进程的隔离。
最开始人们使用的是一种叫做分段(Segmentation)的方法,基本思路是把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间。比如程序A需要10 MB内存,那么我们假设有一个地址从0x00000000到0x00A00000的10MB大小的一个假象的空间,也就是虚拟空间,然后我们从实际的物理内存中分配一个相同大小的物理地址,假设是物理地址0x00100000开始到0x00B00000结束的一块空间。然后我们把这两块相同大小的地址空间一一映射,即虚拟空间中的每个字节相对应于物理空间中的每个字节。这个映射过程由软件来设置,比如操作系统来设置这个映射函数,实际的地址转换由硬件完成。
分段做到了物理隔离且不需要重定位。但是其实并没有解决内存使用效率的问题。当内存不足时,程序需要更换物理空间去运行,整个程序会进行迁移。这样就会有大量的数据换入换出,导致效率十分低下。
但事实上,根据程序的局部性原理,当一个程序在运行时,在某个时间段内,它只是频繁的运用到一小部分数据,也就是说,程序的很多数据其实在一个时间段内是不会被用到的。人们很自然地想到了更小粒度的内存分割和映射的方法,使得程序的局部性原理得到充分的利用,大大提高了内存的使用率。这种方法就是分页(Paging)。
分页的基本方法是把地址空间人为地等分成固定大小的页,每一页的大小由硬件决定,或硬件支持多种大小的页,由操作系统选择决定页的大小。
详细定义:
分页(英语:Paging),是一种操作系统里存储器管理的一种技术,
可以使电脑的主存可以使用存储在辅助存储器中的数据。
操作系统会将辅助存储器(通常是磁盘)中的数据分区成固定大小的区块,称为“页”(pages)。
当不需要时,将分页由主存(通常是内存)移到辅助存储器;
当需要时,再将数据取回,加载主存中。
相对于分段,分页允许存储器存储于不连续的区块以维持文件系统的整齐。
分页是磁盘和内存间传输数据块的最小单位。
比如IntelPentium系列处理器支持4KB或4MB的页大小,那么操作系统可以选择每页大小为4KB,也可以选择每页大小为4MB,但是在同 一时刻只能选择一种大小,所以对整个系统来说,页就是固定大小的。
目前几乎所有的PC上的操作系统都使用4KB大小的页。我们使用的PC机是32位的虚拟地址空间,也就是4GB,那么按4KB每页分的话,总共有1 048 576个页。物理空间也是同样的分法。
那么,当我们把进程的虚拟地址空间按页分割,把常用的数据和代码页装载到内存中,把不常用的代码和数据保存在磁盘里,当需要用到的时候再把它从磁盘里取出来即可。
我们把虚拟空间的页就叫虚拟页(VP,Virtual Page),把物理内存中的页叫做物理页(PP,Physical Page),把磁盘中的页叫做磁盘页(DP,Disk Page)。
如上图所示,每个虚拟空间有8页,每页大小为1KB,那么虚拟地址空间就是8KB。我们假设该计算机有13条地址线,即拥有2^13的物理寻址能力,那么理论上物理空间可以多达8KB。但是出于种种原因,购买内存的资金不够,只买得起6KB的内存,所以物理空间其实真正有效的只是前6KB。
我们假设有两个进程Process1和Process2,它们进程中的部分虚拟页面被映射到了物理页面,比如VP0、VP1和VP7映射到PP0、PP2和PP3;而有部分页面却在磁盘中,比如VP2和VP3位于磁盘的DP0和DP1中;另外还有一些页面如VP4、VP5和VP6可能尚未被用到或访问到,它们暂时处于未使用的状态。
虚拟存储的实现需要依靠硬件的支持,对于不同的CPU来说是不同的。但是几乎所有的硬件都采用一个叫MMU(Memory Management Unit)的部件来进行页映射,