内存管理
原书《操作系统精髓与设计原理——富兰克林》第七章。
内存管理的需求
- 重定位:进程通常没有直接通过物理内存地址访问内存的能力,这是为了保证隔离性和安全,还有提供一定程度的抽象,让程序员不再和物理内存打交道。所以要有一种方式将进程中的逻辑地址映射到物理地址。
- 保护:进程不能访问不属于它的内存区域。
- 共享:一些时候,进程需要共享内存。比如通过
fork
创建一个子进程时,可以先和父进程共享内存区,只有当子进程或父进程要修改时,才将那些数据复制到子进程的内存空间。 - 逻辑组织
- 物理组织:在逻辑上,可访问的内存地址可能被组织成一个连续线性的地址空间,但它们在物理上不一定是连续线性的,而且通常是使用二级存储器——内存+外存。
内存分区
固定分区
将物理空间划分成固定大小的分区,这些分区大小可能一样也可能不一样。
它们都有的问题是:
- 有些过于大的程序无法放置到一个分区中,需要程序设计者自己使用覆盖技术实现装载部分程序到分区中
- 经常会产生内部碎片,因为很少有一个进程的大小等于分区大小,其中有一些内存空间一定是浪费的
放置算法
选择将进程放置到哪个分区的算法,通常使用能够容纳进程的最小分区。防止算法还要解决当目前所有进程都处于阻塞状态,并且系统中没有可以分配给新进程的分区时,将目前内存中的一个进程换出,并装载新进程到内存。
动态分区
动态分区为每个进程分配和它需要的容量一样的空间。
在上面的示例中,物理内存共有64M的空间,操作系统占用一个8MB的分区,进程1需要20M的空间,系统就为它分配20M的分区。进程2和3也是一样的。当进程4想要进来时,需要换出一个进程,操作系统会根据进程大小或已有进程的阻塞状态进行考量,决定要换出的分区。比如图中换出了进程2,留下了6MB的碎片,这种碎片称为外部碎片,因为它在进程4的分区外部。
动态分区随着时间推移会产生越来越多的碎片,需要以一定频率或者一定条件触发内存压缩,即将所有分区整理,消除碎片。内存压缩是很耗时的任务。
放置算法
常见的动态分区放置算法有:最佳适配、首次适配和下次适配。
它们都在内存中选择大于等于需要内存大小的块,不过最佳适配选择离需要内存大小最近的块,首次适配选择第一个碰到的符合要求的块,下次适配从上一次选择的位置开始向下扫描。
一般首次适配算法是最优的,最佳适配算法消耗更多时间在选择分区上,并且由于它太“追求完美”,所以每次都留下一个很小的碎片,所以会加速内存压缩的频率。
伙伴系统
伙伴系统也是一种动态分区的内存管理机制。
- \(2^L\),最小内存块大小
- \(2^U\),最大内存块大小,通常是整个物理内存大小
当进程需要大小为\(s\)的内存空间时,若\(2^{U-1} < s \leq 2^U\),则分配整个空间,否则将剩余空间分成两个\(2^{U-1}\),然后若\(2^{U-2} < s \leq 2^{U-1}\),直接分配,否则继续分成两份。
如上图,一个占用100KB的进程。系统先将1MB的内存分为两个512KB,然后再将一个512分成两个256,然后是128。最终,该进程将被分配到128KB的块中。当再来一个256KB的进程时,因为已经有了一个这样大的块,该块将直接被利用。
当有一个进程被释放时,若它的兄弟也处于空闲状态,它和它的兄弟会合并为一个大块。
重定位
当进程被换出后换入,它所在的分区可能不是以前的分区了,就算不是在基于上面的进程内存管理机制的系统上,也存在这种问题。所以需要一种办法来在进程中使用逻辑地址,然后通过一种机制映射成物理地址。这样程序就不用考虑它所在的分区与之前是否一致问题,它使用的逻辑地址会被自动重定位到物理地址。
重定位需要硬件的支持,一般是一个用来保存进程起始物理地址的基址寄存器和一个用于指定程序在内存中的终止位置的寄存器组成。在分页系统中也有类似的寄存器。
每次CPU遇到程序中地址相关的指令时,都要将逻辑地址加上基址,并与它的终止位置进行比较,若合法才执行该指令,否则产生一个中断。
分页
上面的动态分区技术也只考虑了分割物理内存,没有考虑过将进程本身也分割开,这导致进程只能放置在连续的物理空间中,并且会产生一些稍大的碎片。
分页技术将物理空间分成固定大小(一般是4KB)的页框,又把进程分成和页框一样大小的页。这样就算有碎片,也只会在最后一个页框中有不到一个页面大小的内部碎片。
如上图,内存中有15个可用页框,进程A占用4个页,操作系统把这4个页直接加载到对应的页框。对于进程B和进程C也一样。当加载进程D时,已经没有足够的5个页框了,操作系统必须换出一个进程。这里它选择换出进程B,因为进程也被分割成一个个页,所以,进程可以被分配在不连续的物理空间上。如上图f。
同时因为不保证进程在连续的物理空间上,所以一个基址寄存器和界限寄存器已经无法满足逻辑地址到物理地址的映射了。我们需要一张页表来完成每个页面到物理空间的映射。
比如采用16位地址时,页大小为1KB,就可以使用地址的前6位索引页面,后10位用来定位该逻辑地址在页面中的偏移量。从这中映射方式中也能看出,页面大小必须采用2的幂次。
采用分页系统将进程划分为很小的页,也给虚拟内存的支持提供便利。
分段
目前的理解就是好像和分页差不多,但是分段不要求大小相等,虽然有个最大长度限制。
而且分页对于程序员是透明的,分段不是。编译器或程序员可以使用分段组织程序中的数据,以实现模块化程序设计。
还有分页由于页框大小一致,所以会产生内部碎片,而分段不会,因为它们并不固定大小,所以分段会产生外部碎片。
习题
- 重定位
- 保护
- 共享
- 逻辑组织
- 物理组织
重定位进程是指将进程中使用的逻辑地址映射到具体的物理地址上。这是因为被操作系统换出的进程被再次装载进内存时,并不一定在原先的位置,所以需要使用逻辑地址。
因为编译器并不知道进程会运行在物理内存的实际哪个位置。
有些进程需要共享数据。或者有时我们开启一个运行中进程的副本,为了效率,我们可以让它先访问原始进程的数据,需要时才复制。
避免过多的碎片空间浪费。
内部还是外部的参考在于分区。若碎片空间在分区内部则称为内部分区,否则称为外部分区。
- 逻辑地址(logical address) 是指与当前数据在内存中的物理分配地址无关的访问地址,在执行对内存的访问之前必须把它转换为物理地址。
- 相对地址(relative address) 是逻辑地址的一个特例,它是相对于某些已知点(通常是程序的开始处)的存储单元。
- 物理地址( physical address) 或绝对地址是数据在内存中的实际位置。
页框是槽位,页是放到槽位中的数据。页框是实际物理空间,页是虚拟的。
页大小必须固定,段不用。页对程序透明,段可以由程序员和编译器操作。