当前位置: 首页 > article >正文

操作系统导论——第13章 抽象:地址空间

一、早期系统

       从内存来看,早期的机器并没有提供多少抽象给用户。基本上,机器的物理内存如图13.1所示

        操作系统曾经是一组函数(实际上是一个库),在内存中(在本例中,从物理地址0开始),然后有一个正在运行的程序(进程),目前在物理内存中(在本例中,从物理地址64KB开始), 并使用剩余的内存。这里几乎没有抽象,用户对操作系统的要求也不多。

二、多道程序和时分共享

        由于机器昂贵,人们开始更有效地共享机器。因此,多道程序系统时代开启,其中多个进程在给定时间准备运行,比如当有一个进程在等待 I/O 操作的时候,操作系统会切换这些进程,从而增加了 CPU 的有效利用率

        但很快,分时系统的时代诞生了,许多人意识到批量计算的局限性,尤其是程序员本身,厌倦了长时间的(低效率的)编程——调试循环。交互性变得很重要,因为许多用户可能同时在使用机器,每个人都在等待(或希望)它们执行的任务及时响应。

        一种实现时分共享的方法,是让一个进程单独占用全部内存运行一小段时间(见图 13.1),然后停止它,并将它所有的状态信息保存在磁盘上(包含所有的物理内存),加载其他进程的状态信息,再运行一段时间,这就实现了某种比较粗糙的机器共享。 

        这种方法的问题:太慢,特别是当内存增长的时候。虽然保存和恢复寄存器级的状态信息(程序计数器、通用寄存器等)相对较快,但将全部的内存信息保存到磁盘就太慢了。因此,在进程切换的时候,我们仍然将进程信息放在内存中,这样操作系统可以更有效率地实现时分共享(见图13.2)。

        在图13.2中,有3个进程(A、B、C),每个进程拥有从 512KB 物理内存中切出来给它们的一小部分内存。假定只有一个CPU,操作系统选择运行其中一个进程(比如A),同时其他进程(B和C)则在队列中等待运行。 

       随着时分共享变得更流行,对操作系统又有了新的要求。特别是多个程序同时驻留在内存中,使保护(protection) 成为重要问题不希望一个进程可以读取其他进程的内存,更别说修改

三、地址空间

        操作系统需要提供一个易用的物理内存抽象。这个抽象叫做地址空间,是运行的程序看到的系统中的内存。理解这个基本的操作系统内存抽象,是了解内存虚拟化的关键。

         一个进程的地址空间包含运行的程序的所有内存状态。比如:程序的代码(code,指令) 必须在内存中,因此它们在地址空间里。当程序在运行的时候,利用(stack)来保存当前的函数调用信息分配空间给局部变量传递参数和函数返回值。最后,(heap)用于管理动态分配的、用户管理的内存,就像从C语言中调用 malloc() 或面向对象语言(如C ++ 或Java)中调用new 获得内存。当然,还有其他的东西(例如,静态初始化的变量),但现在假设只有这3个部分:代码、栈和堆

        在图 13.3 的例子中,有一个很小的地址空间(16KB)。程序代码位于地址空间的顶部(本例中从 0 开始,并且装入到地址空间的前 1KB)。代码是静态的(因此很容易放在内存中),所以可以将它放在地址空间的顶部,我们知道程序运行时不再需要新的空间。

        接下来,在程序运行时,地址空间有两个区域可能增长(或者收缩)。它们就是堆(在顶部) 和栈(在底部)。通过将它们放在地址空间的两端,可以允许这样的增长:它们只需要在相反的方向增长。因此堆在代码(1KB) 之下开始并向下增长(当用户通过 malloc()请求更多内存时),栈从 16KB 开始并向上增长(当用户进行程序调用时)。然而,堆栈和堆的这种放置方法只是一种约定,如果你愿意, 可以用不同的方式安排地址空间 [当多个线程(threads)在地址空间中共存时,就没有像这样分配空间的好办法了]。

        当然,描述地址空间时,所描述的是操作系统提供给运行程序的抽象。程序不在物理地址0~16KB的内存中,而是加载在任意的物理地址。回顾图13.2中的进程 A、B和C,可以看到每个进程如何加载到内存中的不同地址。因此问题来了:

                                                 关键问题:如何虚拟化内存

        操作系统如何在单一的物理内存上为多个运行的进程(所有进程共享内存)构建一个私有的、可能很大的地址空间的抽象?

        当操作系统这样做时,我们认为操作系统在虚拟化内存(virtualizing memory),因为运行的程序认为它被加载到特定地址(例如 0)的内存中,并且具有非常大的地址空间(例如 32 位或64位)。现实很不一样。

        例如,当图13.2中的 进程A 尝试在地址0(我们将称其为虚拟地址,virtual address) 执行加载操作时,然而操作系统在硬件的支持下,出于某种原因,必须确保不是加载到物理地址0,而是物理地址320KB(这是A载入内存的地址)。这是内存虚拟化的关键,这是世界上每一个现代计算机系统的基础。

四、目标

        操作系统的工作——虚拟化内存。操作系统不仅虚拟化内存,还有一定的风格。为了确保操作系统这样做,我们需要一些目标来指导。

         虚拟内存(VM)系统的一个主要目标透明(transparency)。操作系统实现虚拟内存的方式,应该让运行的程序看不见。因此,程序不应该感知到内存被虚拟化的事实,相反,程序的行为就好像它拥有自己的私有物理内存。在幕后,操作系统(和硬件)完成了 所有的工作,让不同的工作复用内存,从而实现这个假象。

         虚拟内存的另一个目标是效率(efficiency)。操作系统应该追求虚拟化尽可能高效 ,包括时间上(即不会使程序运行得更慢)和空间上(即不需要太多额外的内存来支持虚拟化)。在实现高效率虚拟化时,操作系统将不得不依靠硬件支持,包括TLB这样的硬件功能。

         虚拟内存第三个目标是保护(protection)。操作系统应确保进程受到保护(protect), 不会受其他进程影响,操作系统本身也不会受进程影响。当一个进程执行加载、存储或指令提取时,它不应该以任何方式访问或影响任何其他进程或操作系统本身的内存内容(即在它的地址空间之外的任何内容)。因此,保护让我们能够在进程之间提供隔离(isolation) 的特性,每个进程都应该在自己的独立环境中运行,避免其他出错或恶意进程的影响。

        在接下来的章节中,我们将重点介绍虚拟化内存所需的基本机制(mechanism),包括硬件和操作系统的支持。我们还将研究一些较相关的策略(policy),你会在操作系统中遇到它们,包括如何管理可用空间,以及在空间不足时哪些页面该释放。通过这些内容,你 会逐渐理解现代虚拟内存系统真正的工作原理①。

五、小结

        介绍了操作系统的一个重要子系统:虚拟内存。虚拟内存系统负责为程序提供一 个巨大的、稀疏的、私有的地址空间的假象,其中保存了程序的所有指令和数据。操作系统在专门硬件的帮助下,通过每一个虚拟内存的索引,将其转换为物理地址,物理内存根据获得的物理地址去获取所需的信息。操作系统会同时对许多进程执行此操作,并且确保程序之间互相不会受到影响,也不会影响操作系统。整个方法需要大量的机制(很多底层机制)和一些关键的策略。 


http://www.kler.cn/a/597129.html

相关文章:

  • Python网络编程入门
  • C语言入门教程100讲(2)变量与常量
  • 随笔(1)
  • 大模型-提示词工程与架构
  • MySQL Router被HTTP流量击穿
  • linux之 内存管理(1)-armv8 内核启动页表建立过程
  • Python协程调度
  • 一文分清重载与重写:Java 编程基础的关键辨析
  • OBOO鸥柏丨广告机终端控制端KylinOS麒麟、统信UOS/鸿蒙国产系统
  • nextjs使用next-intl要注意
  • 整理和总结微信小程序的高频知识点
  • SQL Server2019安装步骤+使用+解决部分报错+卸载(超详细 附下载链接)
  • 【GeeRPC】项目总结:使用 Golang 实现 RPC 框架
  • 可以高效记录工作生活琐事的提醒APP工具
  • 走进Java:String字符串的基本使用
  • SVN无法弹出账号密码
  • Labview和C#调用KNX API 相关东西
  • C++ --- 多态
  • QT开发(4)--各种方式实现HelloWorld
  • 《Git:基本命令使用》