热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

VAX/VMS虚拟内存系统

案例研究:在我们结束对虚拟内存的研究之前,让我们更仔细地研究一下在VAXVMS操作系统中发现的一个特别干净和出色的虚拟内存管理器。在本文中ÿ

案例研究:

在我们结束对虚拟内存的研究之前,让我们更仔细地研究一下在VAX/VMS操作系统中发现的一个特别干净和出色的虚拟内存管理器。

在本文中,我们将讨论这个系统,以说明在一个完整的内存管理器中,前面章节中提出的一些概念。

23.1背景

VAX-11微型计算机体系结构是由数字设备公司(DEC)在1970年代末引入的。DEC在微型计算机时代是计算机行业的一个巨大的参与者;不幸的是,一系列糟糕的决定和个人电脑的出现慢慢地(但肯定地)导致了他们的灭亡[C03]。该架构在许多实现中实现,包括VAX-11/780和功能较弱的VAX-11/750。

系统的操作系统被称为VAX/VMS(或者只是普通的vm),其中一个主要的架构师是Dave Cutler,他后来领导了开发微软Windows NT的工作[C93]。VMS有一个普遍的问题,它将在广泛的机器上运行,包括非常便宜的VAXen(是的,这是正确的复数),在相同的体系结构家庭中使用非常高端和强大的机器。因此,操作系统必须拥有在这一庞大系统中工作(并且运行良好)的机制和策略。

难点:如何避免一般性的诅咒

操作系统通常有一个被称为通用性的问题,他们的任务是广泛支持广泛的应用程序和系统。诅咒的基本结果是操作系统不太可能支持任何一个安装。在VMS的情况下,这个诅咒是非常真实的,因为VAX-11架构是在许多不同的实现中实现的。

虽然操作系统常常依赖硬件来构建高效的抽象和幻象,但有时硬件设计者并不完全正确;在VAX硬件中,我们将看到一些这样的例子,以及VMS操作系统如何构建一个有效的工作系统,尽管存在这些硬件缺陷。.

23.2内存管理硬件

VAX-11为每个进程提供了一个32位的虚拟地址空间,分为512字节的页面。因此,一个虚拟地址由一个23位的VPN和一个9位的偏移量组成。此外,利用VPN的上两部分来区分页面的大小;因此,系统是分页和分割的混合,就像我们以前看到的那样。

地址空间的下半部分称为进程空间,每个进程都是惟一的。在进程空间的前半部分(称为P0)中,找到了用户程序,以及向下扩展的堆。在进程空间的后半部分(P1)中,我们找到了堆栈,它是向上增长的。地址空间的上半部分称为系统空间(S),尽管只使用了其中的一半。受保护的操作系统代码和数据驻留在这里,操作系统以这种方式共享整个进程。

vm设计器的一个主要问题是VAX硬件(512字节)中页面的大小非常小。由于历史原因,这样的大小有一个基本的问题,就是使简单的线性页表过大。因此,vm设计人员的首要目标之一是确保vm不会用页表压倒内存。该系统通过两种方式将压力页表放在内存中。首先,通过将用户地址空间分割为两个,VAX-11为每个流程提供了一个页面表(P0和P1);因此,在堆栈和堆之间的地址空间中未使用的部分不需要页表空间。基础和边界寄存器使用如您所期望的;一个基本寄存器保存该段的页表的地址,并且边界保持它的大小(即。页表条目数。其次,通过在内核虚拟内存中放置用户页表(对于P0和P1,因此是两个进程),操作系统可以进一步降低内存压力。因此,在分配或增加一个页面表时,内核会在内存中分配自己的虚拟内存空间。如果内存受到严重的压力,内核就可以将这些页表的页面交换到磁盘,从而使物理内存可供其他用途使用。

将页表放在内核虚拟内存中意味着地址转换更加复杂。例如,为了在P0或P1中转换一个虚拟地址,硬件必须首先在其页面表中查找该页面的页表条目(该进程的P0或P1页表)。但是,在这样做的时候,硬件可能首先需要查阅系统页表(它存在于物理内存中);通过完成这个转换,硬件可以学习页表的页面地址,然后最终了解所需内存访问的地址。幸运的是,所有这些都是由VAX的硬件管理的TLBs实现的,它通常(希望)可以绕过这个费力的查找。

23.3真实地址空间

研究VMS的一个简单的方面是,我们可以看到一个真实的地址空间是如何构建的(图23.1)。到目前为止,我们已经假定了一个简单的地址空间,只包含用户代码、用户数据和用户堆,但是正如我们可以看到的,一个真实的地址空间显然更加复杂。

另外:为什么空指针访问会导致SEG错误

现在您应该很好地理解了在空指针引用上发生了什么。通过这样做,一个进程生成一个0的虚拟地址


硬件试图在TLB中查找VPN(也为0),并遭受TLB错误。页面表被查询,而VPN 0的条目被发现是无效的。因此,我们有一个无效的访问,它将控制转移到操作系统,这可能会终止进程(在UNIX系统上,进程被发送一个信号,使它们能够对这样的错误作出反应;然而,如果未被捕获,这个过程就会被杀死。

例如,代码段从不在第0页开始。相反,这个页面被标记为不可访问,以便为检测空指针访问提供一些支持。因此,在设计地址空间时需要考虑的一个问题是支持调试,这是不可访问的零页面以某种形式提供的。也许更重要的是,内核虚拟地址空间(即:,它的数据结构和代码)是每个用户地址空间的一部分。在上下文切换中,操作系统更改P0和P1寄存器,以指向即将运行的进程的适当页表;但是,它并没有改变S基础和绑定寄存器,因此相同的内核结构被映射到每个用户地址空间。

由于许多原因,内核被映射到每个地址空间。这种结构使得内核的使用更加轻松。例如,当OS从一个用户程序(例如,在write()系统调用)中传递一个指针时,很容易从该指针复制数据到它自己的结构。操作系统是自然编写和编译的,无需担心其访问的数据来自何处。如果相反,内核完全位于物理内存中,那么将页表的交换页面转换为磁盘将非常困难;如果内核被赋予了自己的地址空间,那么在用户应用程序和内核之间移动数据将再次变得复杂和痛苦。使用这种结构(现在广泛使用),内核几乎可以作为应用程序的库,尽管是受保护的。

关于这个地址空间的最后一点是关于保护的。显然,操作系统不希望用户应用程序读取或写入操作系统数据或代码。因此,硬件必须支持不同的页面保护级别来启用这个功能。VAX通过在页表的保护位中指定CPU必须处于何种特权级别才能访问特定页面。因此,系统数据和代码被设置为比用户数据和代码更高的保护级别;试图从用户代码中访问这些信息将会在操作系统中生成一个陷阱,并且(您猜测)可能会终止这个过程。

23.4页面置换

VAX中的页表条目(PTE)包含以下部分:有效位保护字段(4位)、修改(或脏)位、为OS使用预留的字段(5位),最后一个物理帧编号(PFN)存储物理内存中页面的位置。精明的读者可能会注意到:没有参考位!因此,vm替换算法必须在没有硬件支持的情况下决定哪些页面是活动的。开发人员还担心内存占用,这些程序占用大量内存,使其他程序难以运行。迄今为止,我们所看到的大多数政策都很容易受到这种束缚;例如,LRU是一个全局策略,它不公平地在进程之间共享内存。

分段FIFO

为了解决这两个问题,开发人员提出了分段FIFO替换策略[RL81]这个想法很简单:每个进程有一个最大的页面数,它可以保存在内存中,也就是它的驻留集大小(RSS)每个页面都保存在FIFO列表中;当一个进程超过它的RSS时,第一个页面被逐出。FIFO显然不需要来自硬件的任何支持,因此很容易实现。当然,纯粹的FIFO并没有表现得特别好,正如我们之前看到的。为了提高FIFO的性能,VMS引入了两个第二次机会列表,其中页面被放置在被逐出内存之前,特别是一个全球清洁页面自由列表和dirty-page列表。当一个进程P超过它的RSS时,一个页面将从它的每个进程FIFO中删除;如果清洗(未修改),则放置在清洁页面列表的末尾;如果脏(修改),它被放置在dirty-page列表的末尾。如果另一个进程Q需要一个空闲页面,它将从全局清除列表中获得第一个空闲页面。但是,如果在该页面被回收之前的原始进程P错误,P就会从空闲的(或脏的)列表中重新声明它,从而避免了昂贵的磁盘访问。这些全局第二次机会列表越大,FIFO算法对LRU的执行越紧密[RL81]。


  • 页面聚类

vm中使用的另一个优化也有助于克服vm中的小页面大小。特别地,在这样小的页面中,在交换过程中磁盘I/O可能会非常低效,因为磁盘在大量传输时做得更好。为了使交换I/O更高效,vm添加了许多优化,但最重要的是集群。通过集群,VMS将大量的页面从全局的脏列表中组合起来,并在一个瞬间将它们写入磁盘(从而使它们变得干净)。

集群在大多数现代系统中被使用,因为在交换空间内放置页面的自由可以让操作系统组页面,执行更少和更大的写入,从而提高性能。

23.5其他整洁的VM技巧。

VMS还有另外两种现在标准的方法:要求零和复制。我们现在来描述这些延迟优化。为了更好地理解这一点,让我们考虑在您的地址空间中添加一个页面的例子,比如在您的堆中。在一个简单的实现中,操作系统响应一个请求,通过在物理内存中找到一个页面,将页面添加到您的堆中(安全性要求;否则,您将能够在其他进程使用它时看到页面上的内容!),然后将其映射到您的地址空间(即:,将页表设置为所需的物理页面。但是这种简单的实现可能代价高昂,特别是如果该页面不被流程使用的话。随着需求的减少,当页面被添加到你的地址空间时,操作系统会做的非常少;它在页表中添加了一个条目,该条目标记了无法访问的页面。如果进程读取或写入页面,则会出现一个陷阱。在处理这个陷阱时,操作系统会注意到(通常是通过在页面表条目的OS部分预留的一些字节),这实际上是一个需求为零的页面;此时,操作系统需要找到一个物理页面,将其调零,并将其映射到进程的地址空间中。如果进程从不访问页面,那么所有的工作都将被避免,因此需求的优点是零。

懒惰在生活和操作系统中都是一种美德。懒惰可以推迟到后来,这在一个操作系统中是有益的,有很多原因。首先,推迟工作可能会减少当前操作的延迟,从而提高响应能力;例如,操作系统经常报告给文件的写操作立即成功,并且只在后台将它们写入磁盘。其次,更重要的是,懒惰有时会使我们完全不需要做这项工作;例如,在删除文件之前延迟写,这样就不需要进行写操作了。懒惰在生活中也很好:例如,通过推迟你的操作系统项目,你可能会发现你的同学们已经发现了项目规范bug。但是,这个类项目不太可能被取消,所以太懒可能会有问题,导致项目延迟,糟糕的成绩,和一个悲伤的教授。不要让教授伤心。

在VMS中发现的另一个很酷的优化(实际上,在所有现代操作系统中)-写入时复制
(短的牛)。这个想法至少可以追溯到TENEX操作系统[BB+72],很简单:当操作系统需要从一个地址空间复制一个页面到另一个地址空间时,它可以将它映射到目标地址空间,并在两个地址空间中标记它。
如果两个地址空间只读取页面,则不会采取进一步的操作,因此操作系统在不实际移动任何数据的情况下实现了快速复制。

但是,如果其中一个地址空间确实试图写入页面,那么它将陷进操作系统中。OS将会注意到页面是一个牛页面,因此(lazily)分配一个新页面,填充数据,并将这个新页面映射到错误进程的地址空间中。然后这个过程继续,现在有了它自己的页面的私有副本。

的用处有很多。当然,任何类型的共享库都可以被映射到许多进程的地址空间中,节省宝贵的内存空间。当然,任何类型的共享库都可以被映射到许多进程的地址空间中,节省宝贵的内存空间。在UNIX系统中,由于fork()和exec()的语义,COW更加重要。您可能记得,fork()创建了调用者的地址空间的精确副本;有了很大的地址空间,这样的拷贝是缓慢的和数据密集的。更糟糕的是,大部分的地址空间都被随后的exec()调用所覆盖,而exec()将调用进程的地址空间覆盖到即将执行的d程序。通过执行copy-on-write fork(),操作系统避免了大量不必要的复制,从而在提高性能的同时保留了正确的语义。

现在您已经看到了对整个虚拟内存系统的全面审查。希望大多数细节都很容易理解,因为您应该已经对大多数基本机制和策略有了很好的理解。列维和利普曼的优秀(和简短)论文中有更多的细节[LL82];我们鼓励大家阅读它,这是一种很好的方式来看看这些章节背后的原始材料是什么样子的。

在可能的情况下,您还应该通过阅读关于Linux和其他现代系统的知识来了解更多关于该技术的状态。那里有很多的原始资料,包括一些合理的书籍[BC05]。有一件事会让你大吃一惊:在VAX/VMS上的旧论文中发现的经典思想仍然影响着现代操作系统的构建。









推荐阅读
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • Linux下安装免费杀毒软件ClamAV及使用方法
    本文介绍了在Linux系统下安装免费杀毒软件ClamAV的方法,并提供了使用该软件更新病毒库和进行病毒扫描的指令参数。同时还提供了官方安装文档和下载地址。 ... [详细]
author-avatar
mobiledu2502877493
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有