抽象:地址空间

Friday, October 25, 2019

地址空间

一个进程的地址空间包含运行的程序的所有状态,比如代码,栈,堆。代码是静态的,可以放在顶部,堆和栈都会增长,所以可以放在两头。

那么,操作系统如何在单一的物理内存,为多个运行的内存构建一个私有的,很大的地址空间的抽象。

进程之间的内存空间必须隔离,且内存地址都是虚拟的,比如在内存地址0进行加载时,必然不是真实的物理地址0,是操作系统进行了虚拟化。

虚拟化内存

  • 虚拟内存系统的主要目标是透明,实现虚拟内存的方式,不被程序所感知,以为就在一个连续的私有物理内存中。

  • 另一个目标是效率,在时间上(不会变得太慢)和空间(不会占用太多额外空间)上。

  • 第三个目标是保护,进程和操作系统本身能够受到保护,有隔离性。

地址转换

为了高效,灵活地虚拟化内存,我们用地址转换这一种技术来实现。硬件对每次内存访问进行处理,将指令中的虚拟地址转换为实际存储的物理地址。

操作系统必须管理内存,记录被占用和空闲的地址,保持对内存使用的控制。

假设用户的地址空间是连续放在物理内存中

void func() {
   int x;
   x = x + 3;
}

这段简单的代码,发生了以下几次内存访问:

  • 从地址128(代码第一行存放的地址)获取指令
  • 执行指令(从地址15kb栈的地址中加载数据)
  • 从地址132(代码下一行)获取命令
  • 执行命令(没有内存访问)
  • 从地址135获取指令
  • 执行指令(新值存入地址15kb)

对于程序而言,这是一段从0到16kb 的地址空间,所有的内存引用都在这个范围。但是实际上,操作系统将这个进程空间并不一定从0开始。操作系统怎么实现转换。

OS15-1

基于硬件的动态重定位

CPU 需要两个硬件寄存器:基址(base)和界限(bound)寄存器。 在程序执行时,操作系统会决定物理内存中实际的加载地址,将起始地址记录在基址寄存器中。 比如一条指令的地址为128,当需要获取这条指令时,先将这个值加上基址寄存器的值,得到实际的物理地址。再比如,从15kb 的地址加载数据,处理器同样会将虚拟地址加上基址寄存器的值,最终得到物理地址。这就是地址转换。

界限寄存器就是提供了访问保护,当超出这个值之后,CPU 就会触发异常。这要确保进程产生的所有地址都在界限中。

我们也称这个为内存管理单元。MMU

OS15.2

操作系统的问题

硬件添加了新功能,使得操作系统也有一些问题需要处理,要跟硬件支持结合在一起,来实现一个简单的虚拟内存。

  • 进程创建时,操作系统必须为进程的地址空间找到内存空间。操作系统可以把物理内存标记为空闲或者已用,进程创建时,检索这个空闲列表,找到位置后,标记为已用。
  • 进程终止时,操作系统也必须回收它的所有内存,并放回到空闲列表。
  • 上下文切换时,操作系统也要有些额外操作,每个 CPU 只有一个基址寄存器和界限寄存器,在切换进程时,操作系统必须保存和恢复基址和界限寄存器。比如决定切换时,操作系统必须将当前的基址和界限寄存器的内容保存在内存中,放在某种每个进程都有的数据结构中,如进程结构或进程控制块中。恢复进程也是同理。

进程停止时,也可以改变地址空间的物理地址,操作过程类似上下文切换。

操作系统也必须提供异常处理程序。当进程视图越界访问,就要终止错误进程。

OS

内存分段

调度:多级反馈队列