本文是《OSTPE》的笔记,由浅入深介绍计算机中的内存虚拟化技术。
为了实现隔离性以及编程的简单性,操作系统提供内存虚拟化技术,给每个进程制造自己在独占内存的假象。
基址、界限寄存器
假设:
- 为所有进程分配同样大小的内存空间
- 该空间小于物理内存空间
- 进程地址空间保存在连续的内存中
在这三个假设的前提下,可以把物理内存分成若干槽位,下面的图将物理内存分为16KB大小的槽位,操作系统在0——16KB运行,一个进程在32——48KB运行,16——32、48——68KB是空闲的。
重定向地址&检测访问越界
这里采取的办法和进程调度中的办法一样,就是硬件提供支持。CPU提供两个额外的寄存器,基址寄存器和界限寄存器,在操作系统决定要开启一个进程时,它会:
- 确定好该进程放置在物理内存的哪个槽位中
- 将该槽位的起始地址写到基址寄存器
- 将该槽位的结束地址写到界限寄存器
当CPU执行进程的访存指令时,它会:
- 将访存指令中携带的内存地址取出
- 与基址寄存器中的做加法,得到物理地址(称为地址转换,CPU中负责地址转换的单元叫MMU)
- 检查该物理地址是否在基址寄存器和界限寄存器的范围内
- 如果一切正常,按物理地址访问
请注意这里面操作系统和CPU各自的职责
操作系统还应该做什么
- 维护物理内存槽位的未使用列表
freelist
- 在CPU调度其它进程(换出当前进程)时,操作系统的时钟中断处理程序应该保存进程的基址寄存器或界限寄存器
- 启动时必须向硬件注册内存管理相关的异常处理程序表
执行示意
注意,因为进程指令也在内存中,所以每次取值也需要将虚拟地址转换成物理地址
优缺点
优点:
- 简单,优雅
缺点:
- 所有进程空间一致,即使它没有使用,该空间也分配出去了,栈和堆之间的空闲空间不能分配给其它进程(内部碎片严重)
- 所有进程空间一致,当它不够用时,没法动态扩缩容
- 这个实现可以移动进程在物理空间的位置,修改基址和界限寄存器就行,但必须整体移动
分段
分段主要解决上一个解决办法中的堆栈之间有很大的内部碎片的问题。
分段即将进程地址空间分为多个逻辑段,比如分为栈段、堆段和代码段,每个段映射到物理内存空间的不同区域。
需要什么
- 每个段一个基址寄存器,一个界限寄存器
- 一个确定进程当前访问的内存地址在哪个段中的方式
- 显式方式:占用虚拟地址中的几位来区分
- 隐式方式:通过地址的产生方式区分,若地址来自PC,则找代码段
- 不同的地址映射逻辑(因为栈是反向增长的)
说下第三点
假设这个栈段被分配到物理地址的26KB——28KB,对于反向增长的栈来说,它的起始地址是28KB,而结束地址是26KB,所以操作系统在对栈进行地址转换时应该做一些额外的处理。
段共享
假设你运行两个WORD程序,它们两个的代码段应该是一模一样的,这时,操作系统可以只在物理内存中保存一个代码段,然后将两个WORD的代码段的基址寄存器和界限寄存器设置成相同的。
保护位
硬件应该为每个段增加保护位来确定进程对某些段的访问权限,比如:
段错误(Segmentation Fault)
即你访问进程所有的段外的内存地址。
优缺点
优点:
- 没有内部碎片
缺点:
- 实现较之前稍微复杂
- 具有大量外部碎片(看采用的freelist管理算法)
- 书上好像没提到段是否能动态扩容,如果不能,那堆栈内部不还是有已分配但没用上的空间嘛
未完...
标签:操作系统,虚拟化,基址,地址,内存,寄存器,进程 From: https://www.cnblogs.com/lilpig/p/16786756.html