内存超载解决方案

  1. 交换(swap)
  2. 虚拟内存(virtual memory)

第一种方案的问题

  1. 交换时会导致产生多个空闲区(也称为空洞,hole,类似于磁盘碎片),通过把所有的进程尽可能向下移动,有可能将这些小的空闲区域合成一大块,该技术被称为内存紧缩(memory compaction)

    但内存紧缩非常耗时,例如,一台 1GB 内存大小的计算机每 20ns 可以复制 4 个字节,它紧缩全部内存要大约花费 5s。所以内存紧缩操作通常不进行

  2. 进程在运行时使用内存常常需要动态增长,如果有相邻的足够使用的空闲区域则没问题,如果相邻内存空间被另一个进程占用,则只能将这个进程移到一段更大的内存中去,如果没有这么大的空间,那么这个进程只能阻塞等待空间,或者死掉

    如果大部分运行的进程都需要动态增长内存,那么为了减少进程内存搬运的开销,一种可行的方案是,当换入和移动进程时为它多分配一些预留的内存

  3. 速度,典型的 SATA 硬盘读写速度一般不过 100MB/s,意味着大约 10s 才能换出一个 1GB 的程序,再花另一个 10s 才能再将一个 1GB 的程序换入

空闲内存管理

  1. 位图

    划分内存为许多个小的单位,每个单位用 0 或 1 记录是否空闲

    划分单元的大小是一个重要的设计因素。在位图大小和内存单元大小之间要取平衡,且进程的大小一定要是划分单元的整数倍,不然在最后一个分配单元中就会有一定数量的内存被浪费

  2. 空闲链表

    若 H 代表空闲,P 代表占用

    链表可以形如:head: {H, start, length, next} -> next: {P, start, length, next2} -> …

    tip: 使用双链表效率会更高

    按照以上链表方式管理内存时,为新进程分配内存的算法有以下几种

    1. 首次适配(first fit)
    2. 下次适配(first fit)
    3. 最佳适配(best fit)
    4. 最差适配(worst fit)

    另外一种 “快速适配(quick fit)” 的算法为一些常用大小的空闲区维护单独的链表

虚拟内存

一段有意思的故事:交换技术到虚拟内存的过渡期间,有一种方案,将程序分割成多个片段,这些片段被称为覆盖(overlay),然后像交换整个进程一样每次只交换这些覆盖(overlay),不过这必须要码农们自己管理好程序分割,枯燥又困难,于是懒人们就搞出来了这个自动化的方案

基本思想

每个程序都有自己独立的地址空间,这个空间被分为许多块,每一块被称为一页或页面(page)。每一页有连续的地址范围。这些页被映射到物理内存,但并不是所有的页都被映射到物理内存才能运行程序。当程序引用到一部分已经在物理内存的地址空间时,硬件立即执行操作;而如果引用到一些不在物理内存中的地址空间时,操作系统将会把缺失的部分装入物理内存并重新执行失败的指令

分页(paging)

虚拟地址空间片段被称为页(page),物理内存也被 MMU(Memory Management Unit)逻辑上划分为页框(page frame),大小一般与页一致。当程序访问了没有对应页框的页时,MMU 注意到该页没有被映射,于是使 CPU 陷入操作系统,称为 缺页中断(page fault) 。操作系统将会找一个很少使用的页框把它的内容写入磁盘(如果它不在磁盘上),然后把要访问的页装入页框,修改映射关系,然后重新启动引起陷阱的指令

页表

页表的作用是把虚拟页面映射为页框

可以把页表当成一个函数
input:虚拟页号
output:物理页框

页表项的结构与机器密切相关,但不同机器的页表项存储的信息都大致相同

现代操作系统笔记-第三章

  • 高速缓存禁止位:0/1 是否允许高速缓存
  • 访问位:
  • 修改位:标明页面是否被修改,如果被修改过,则操作系统重新分配页框时将必须先把该页内容写入磁盘,否则直接简单丢弃即可
  • 保护位:用于指出一个页允许什么类型的访问,比如读、写、执行等

加速分页过程

在任何分页式系统中,都需要考虑两个主要问题
1. 虚拟地址到物理地址的映射必须非常快
2. 如果虚拟地址空间很大,那么页表也会很大

有个容易忘的点:每个进程都有自己的一个页表,因为每个进程都有自己的虚拟地址空间

两个极端的例子
1. 加载一个进程的时候,把该进程的页表直接塞进一个专门的寄存器,接下来执行指令时只需从寄存器读取页表即可,不用访问内存。不过这种方法缺点很明显,一是当页表非常大时,成本很高,二是发生进程切换时,要重新加载另一个进程的页表,代价高昂
2. 整个页表都存在内存中,这样只需一个存储页表起始地址的寄存器即可,这样进程切换时,只需修改寄存器的值。缺点同样很明显,这样每次执行指令时,都必须访问内存,以完成页表项的装入,速度非常慢

转换检测缓冲区(相联存储器)

首先有这样一个事实

大多数程序总是对少量的页面进行多次的访问
因此可以把常常访问的少量页面的对应页表项存入一个高速硬件设备,用到时直接从该高速硬件设备中读

这个设备称为转换检测缓冲区(Translation Lookaside Buffer, TLB),也被称为相联存储器(associate memory),它通常存在于 MMU 中,包含少量的表项,在实际中很少会超过 64 个表项

现代操作系统笔记-第三章

软件 TLB 管理

由操作系统实现页面管理,TLB 表项被操作系统显式装载

针对大内存的页表

多级页表

现代操作系统笔记-第三章

如图所示,32 位的虚拟地址呗划分为 10 位的 PT1 域、 10 位的 PT2 域和 12 位的 Offset(偏移量) 域。因为 偏移量是 12 位,所以页长为 2^12 = 4KB

引入多级页表的目的是避免把全部页表一直保存在内存中,特别是那些从不需要的页表。例如一个需要 12MB 内存的程序,其底端是 4MB 程序正文段,上面是 4MB 的数据段,顶端是 4MB 的堆栈段,在数据段和堆栈段之间是大量根本没用使用的空闲区

倒排页表(inverted page table)

如同名字一样,反向思维,页表不再是每页一条记录,而是每个页框一条记录,表项包含页框号、进程号、页号等信息


未完待续

相关文章: